// AtomTrans.java (c) 2002 by Paul Falstad, www.falstad.com. // Rendering algorithm in this applet is based on the description of // the algorithm used in Atom in a Box by Dean Dauger (www.dauger.com). // We raytrace through a 3-d dataset, sampling a number of points and // integrating over them using Simpson's rule. import java.io.InputStream; import java.awt.*; import java.awt.image.ImageProducer; import java.applet.Applet; import java.applet.AudioClip; import java.util.Vector; import java.util.Hashtable; import java.util.Enumeration; import java.io.File; import java.util.Random; import java.awt.image.*; import java.lang.Math; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.awt.event.*; class AtomTransCanvas extends Canvas { AtomTransFrame pg; AtomTransCanvas(AtomTransFrame p) { pg = p; } public Dimension getPreferredSize() { return new Dimension(300,400); } public void update(Graphics g) { pg.updateAtomTrans(g); } public void paint(Graphics g) { pg.updateAtomTrans(g); } }; class AtomTransLayout implements LayoutManager { public AtomTransLayout() {} public void addLayoutComponent(String name, Component c) {} public void removeLayoutComponent(Component c) {} public Dimension preferredLayoutSize(Container target) { return new Dimension(500, 500); } public Dimension minimumLayoutSize(Container target) { return new Dimension(100,100); } public void layoutContainer(Container target) { int barwidth = 0; int i; for (i = 1; i < target.getComponentCount(); i++) { Component m = target.getComponent(i); if (m.isVisible()) { Dimension d = m.getPreferredSize(); if (d.width > barwidth) barwidth = d.width; } } Insets insets = target.insets(); int targetw = target.size().width - insets.left - insets.right; int cw = targetw-barwidth; int targeth = target.size().height - (insets.top+insets.bottom); target.getComponent(0).move(insets.left, insets.top); target.getComponent(0).resize(cw, targeth); cw += insets.left; int h = insets.top; for (i = 1; i < target.getComponentCount(); i++) { Component m = target.getComponent(i); if (m.isVisible()) { Dimension d = m.getPreferredSize(); if (m instanceof Scrollbar) d.width = barwidth; if (m instanceof Choice && d.width > barwidth) d.width = barwidth; if (m instanceof Label) { h += d.height/5; d.width = barwidth; } m.move(cw, h); m.resize(d.width, d.height); h += d.height; } } } }; public class AtomTrans extends Applet implements ComponentListener { static AtomTransFrame ogf; void destroyFrame() { if (ogf != null) ogf.dispose(); ogf = null; repaint(); } boolean started = false; public void init() { addComponentListener(this); } public static void main(String args[]) { ogf = new AtomTransFrame(null); ogf.init(); } void showFrame() { if (ogf == null) { started = true; ogf = new AtomTransFrame(this); ogf.init(); repaint(); } } public void paint(Graphics g) { String s = "Applet is open in a separate window."; if (!started) s = "Applet is starting."; else if (ogf == null) s = "Applet is finished."; else ogf.show(); g.drawString(s, 10, 30); } public void componentHidden(ComponentEvent e){} public void componentMoved(ComponentEvent e){} public void componentShown(ComponentEvent e) { showFrame(); } public void componentResized(ComponentEvent e) {} public void destroy() { if (ogf != null) ogf.dispose(); ogf = null; repaint(); } }; class AtomTransFrame extends Frame implements ComponentListener, ActionListener, AdjustmentListener, MouseMotionListener, MouseListener, ItemListener { Thread engine = null; Dimension winSize; Image dbimage, memimage; Random random; int gridSizeX = 200; int gridSizeY = 200; public String getAppletInfo() { return "AtomTrans by Paul Falstad"; } Button blankButton; Checkbox stoppedCheck; CheckboxMenuItem colorCheck; CheckboxMenuItem eCheckItem; CheckboxMenuItem xCheckItem; CheckboxMenuItem restrictItem; CheckboxMenuItem dimensionsItem; CheckboxMenuItem axesItem; CheckboxMenuItem autoZoomItem; CheckboxMenuItem animatedZoomItem; MenuItem exitItem; Choice sliceChooser, freqChooser; Choice dirChooser, setupChooser; static final int SLICE_NONE = 0; static final int SLICE_X = 1; static final int SLICE_Y = 2; static final int SLICE_Z = 3; static final int STATE_1S = 0; static final int STATE_2S = 1; static final int STATE_2P1 = 2; static final int STATE_2PX = 3; static final int STATE_2PZ = 4; static final int STATE_3S = 5; static final int STATE_2PM1 = 6; static final int VIEW_XY = 0; static final int VIEW_XZ = 1; static final int VIEW_XZ12 = 2; static final int FREQ_12 = 0; static final int FREQ_13 = 1; static final int FREQ_14 = 2; static final int FREQ_23 = 3; static final int FREQ_24 = 4; static final int FREQ_34 = 5; Scrollbar speedBar, strengthBar, stepBar; Scrollbar resolutionBar; Scrollbar internalResBar; Scrollbar brightnessBar; Scrollbar scaleBar; Scrollbar sampleBar; View viewPotential, viewX, viewStates; View viewList[]; int viewCount; Orbital orbitals[]; int orbCount; Phasor phasors[]; int phasorCount; BasisState states[]; int stateCount; Complex newcoef[]; double dragZoomStart, lastXRot, lastYRot, colorMult; double zoom; // was 10 double rotmatrix[]; Rectangle viewAxes, viewField; static final double pi = 3.14159265358979323846; static final double pi2 = pi*2; static final double root2 = 1.41421356237309504880; static final double root2inv = .70710678118654752440; int xpoints[]; int ypoints[]; int selectedPaneHandle; double freqMax, freq, freqPhase; PhaseColor phaseColors[]; Color purple; double resadj; boolean dragging = false; MemoryImageSource imageSource; int pixels[]; int sampleCount; int dataSize; static int maxModes = 10; static int maxDispCoefs = 8; static int viewDistance = 12; int pause; AtomTrans applet; State selectedState; Phasor selectedPhasor; int selection = -1; static final int SEL_NONE = 0; static final int SEL_POTENTIAL = 1; static final int SEL_X = 2; static final int SEL_STATES = 3; static final int SEL_HANDLE = 4; static final int MODE_ANGLE = 0; static final int MODE_SLICE = 1; static final int DIR_X = 0; static final int DIR_Y = 1; static final int DIR_Z = 2; static final int DIR_CCW = 3; static final int DIR_CW = 4; int slicerPoints[][]; double sliceFaces[][]; double sliceFace[]; int sliceFaceCount; double sliceval = 0; int sampleMult[]; boolean selectedSlice; boolean settingScale; double magDragStart; int dragX, dragY, dragStartX, dragStartY; double t = 0; public static final double epsilon = .01; static final int panePad = 4; static final int phaseColorCount = 50; float funcr, funci; int phiIndex; double bestBrightness, userBrightMult = 1; boolean manualScale; Color gray2; FontMetrics fontMetrics; int getrand(int x) { int q = random.nextInt(); if (q < 0) q = -q; return q % x; } AtomTransCanvas cv; AtomTransFrame(AtomTrans a) { super("Atomic Dipole Transitions Applet v1.5a"); applet = a; } boolean useBufferedImage = false; public void init() { gray2 = new Color(127, 127, 127); String os = System.getProperty("os.name"); String jv = System.getProperty("java.version"); boolean altRender = false; int res = 64; setLayout(new AtomTransLayout()); cv = new AtomTransCanvas(this); cv.addComponentListener(this); cv.addMouseMotionListener(this); cv.addMouseListener(this); add(cv); MenuBar mb = new MenuBar(); Menu m = new Menu("File"); mb.add(m); m.add(exitItem = getMenuItem("Exit")); m = new Menu("View"); mb.add(m); m.add(eCheckItem = getCheckItem("Energy")); eCheckItem.setState(true); m.add(xCheckItem = getCheckItem("Position")); xCheckItem.setState(true); xCheckItem.disable(); m.addSeparator(); m.add(colorCheck = getCheckItem("Phase as Color")); colorCheck.setState(true); m = new Menu("Options"); mb.add(m); restrictItem = getCheckItem("Restrict to N1, N2"); restrictItem.setState(true); m.add(dimensionsItem = getCheckItem("Show Dimensions")); m.add(axesItem = getCheckItem("Show Axes")); axesItem.setState(true); autoZoomItem = getCheckItem("Auto Scale"); autoZoomItem.setState(true); animatedZoomItem = getCheckItem("Animated Scaling"); animatedZoomItem.setState(true); setMenuBar(mb); int i; setupChooser = new Choice(); for (i = 0; setupStrings[i] != null; i++) setupChooser.add(setupStrings[i]); setupChooser.addItemListener(this); add(setupChooser); sliceChooser = new Choice(); sliceChooser.add("No Slicing"); sliceChooser.add("Show X Slice"); sliceChooser.add("Show Y Slice"); sliceChooser.add("Show Z Slice"); sliceChooser.addItemListener(this); add(sliceChooser); freqChooser = new Choice(); freqChooser.add("Freq = n=1 <-> n=2"); freqChooser.add("Freq = n=1 <-> n=3"); freqChooser.add("Freq = n=1 <-> n=4"); freqChooser.add("Freq = n=2 <-> n=3"); freqChooser.add("Freq = n=2 <-> n=4"); freqChooser.add("Freq = n=3 <-> n=4"); freqChooser.addItemListener(this); //add(freqChooser); dirChooser = new Choice(); dirChooser.add("X Dir"); dirChooser.add("Y Dir"); dirChooser.add("Z Dir"); dirChooser.add("CCW Circular"); dirChooser.add("CW Circular"); dirChooser.addItemListener(this); //add(dirChooser); stoppedCheck = new Checkbox("Stopped"); stoppedCheck.addItemListener(this); add(stoppedCheck); add(new Label("Simulation Speed", Label.CENTER)); add(speedBar = new Scrollbar(Scrollbar.HORIZONTAL, 6, 1, 1, 200)); speedBar.addAdjustmentListener(this); new Label("Steps", Label.CENTER); stepBar = new Scrollbar(Scrollbar.HORIZONTAL, 40, 1, 1, 300); stepBar.addAdjustmentListener(this); new Label("Radiation Intensity", Label.CENTER); strengthBar = new Scrollbar(Scrollbar.HORIZONTAL, 130, 1, 85, 200); strengthBar.addAdjustmentListener(this); add(new Label("Brightness", Label.CENTER)); add(brightnessBar = new Scrollbar(Scrollbar.HORIZONTAL, 240, 1, 1, 4000)); brightnessBar.addAdjustmentListener(this); add(new Label("Image Resolution", Label.CENTER)); add(resolutionBar = new Scrollbar(Scrollbar.HORIZONTAL, res, 2, 20, 240)); resolutionBar.addAdjustmentListener(this); /*add(new Label("Internal Resolution", Label.CENTER)); add(internalResBar = new Scrollbar(Scrollbar.HORIZONTAL, res, 2, 20, 200)); internalResBar.addAdjustmentListener(this);*/ new Label("Scale", Label.CENTER); scaleBar = new Scrollbar(Scrollbar.HORIZONTAL, 75, 1, 5, 1620); scaleBar.addAdjustmentListener(this); /*add(new Label("Samples", Label.CENTER)); add(sampleBar = new Scrollbar(Scrollbar.HORIZONTAL, 7, 1, 0, 20)); sampleBar.addAdjustmentListener(this);*/ add(new Label("http://www.falstad.com", Label.CENTER)); try { String param = applet.getParameter("PAUSE"); if (param != null) pause = Integer.parseInt(param); } catch (Exception e) { } int j; phaseColors = new PhaseColor[8*phaseColorCount]; for (i = 0; i != 8; i++) for (j = 0; j != phaseColorCount; j++) { double ang = Math.atan(j/(double) phaseColorCount); phaseColors[i*phaseColorCount+j] = genPhaseColor(i, ang); } purple = new Color(192, 60, 206); slicerPoints = new int[2][5*2]; sliceFaces = new double[4][3]; rotmatrix = new double[9]; rotmatrix[0] = rotmatrix[4] = rotmatrix[8] = 1; rotate(0, -pi/2); xpoints = new int[4]; ypoints = new int[4]; setupSimpson(); setupStates(); doSetup(); random = new Random(); reinit(); cv.setBackground(Color.black); cv.setForeground(Color.white); resize(580, 500); handleResize(); Dimension x = getSize(); Dimension screen = getToolkit().getScreenSize(); setLocation((screen.width - x.width)/2, (screen.height - x.height)/2); show(); } void setupStates() { int maxn = 5; stateCount = maxn*(maxn+1)*(2*maxn+1)/6; int i; states = new BasisState[stateCount]; int n = 1; int l = 0; int m = 0; for (i = 0; i != stateCount; i++) { BasisState bs = states[i] = new BasisState(); bs.index = i; bs.elevel = -1/(2.*n*n); bs.n = n; bs.l = l; bs.m = m; if (m < l) m++; else { l++; if (l < n) m = -l; else { n++; l = m = 0; } } } } MenuItem getMenuItem(String s) { MenuItem mi = new MenuItem(s); mi.addActionListener(this); return mi; } CheckboxMenuItem getCheckItem(String s) { CheckboxMenuItem mi = new CheckboxMenuItem(s); mi.addItemListener(this); return mi; } PhaseColor genPhaseColor(int sec, double ang) { // convert to 0 .. 2*pi angle ang += sec*pi/4; // convert to 0 .. 6 ang *= 3/pi; int hsec = (int) ang; double a2 = ang % 1; double a3 = 1.-a2; PhaseColor c = null; switch (hsec) { case 6: case 0: c = new PhaseColor(1, a2, 0); break; case 1: c = new PhaseColor(a3, 1, 0); break; case 2: c = new PhaseColor(0, 1, a2); break; case 3: c = new PhaseColor(0, a3, 1); break; case 4: c = new PhaseColor(a2, 0, 1); break; case 5: c = new PhaseColor(1, 0, a3); break; } return c; } void setupSimpson() { sampleCount = 15; // 9; // 15; //sampleCount = sampleBar.getValue()*2+1; //System.out.print("sampleCount = " + sampleCount + "\n"); // generate table of sample multipliers for efficient Simpson's rule sampleMult = new int[sampleCount]; int i; for (i = 1; i < sampleCount; i += 2) { sampleMult[i ] = 4; sampleMult[i+1] = 2; } sampleMult[0] = sampleMult[sampleCount-1] = 1; } void handleResize() { reinit(); } void reinit() { setResolution(); Dimension d = winSize = cv.getSize(); if (winSize.width == 0) return; dbimage = createImage(d.width, d.height); setupDisplay(); } void createPhasors() { phasorCount = 0; int i; if (viewStates == null) return; int sz = viewStates.height/4; int x = 0; int y = viewStates.y; int n = 1, l = 0, m = 0; sz = viewStates.height/5; phasorCount = 5+12+15; phasors = new Phasor[phasorCount]; int pct = 0; for (i = 0; i != stateCount; i++) { if (l < 3) { Phasor ph = phasors[pct] = new Phasor(x, y, sz, sz); pct++; ph.state = states[i]; x += sz; } if (++m > l) { x += sz; l++; m = -l; if (l >= n) { x = 0; y += sz; n++; l = m = 0; } } } // in case the states changed createOrbitals(); } void setupDisplay() { if (winSize == null) return; int potsize = (viewPotential == null) ? 50 : viewPotential.height; int statesize = (viewStates == null) ? 64 : viewStates.height; viewX = viewPotential = viewStates = null; viewList = new View[10]; int i = 0; if (eCheckItem.getState()) viewList[i++] = viewPotential = new View(); if (xCheckItem.getState()) viewList[i++] = viewX = new View(); viewList[i++] = viewStates = new View(); viewCount = i; int sizenum = viewCount; int toth = winSize.height; // preserve size of potential and state panes if possible if (potsize > 0 && viewPotential != null) { sizenum--; toth -= potsize; } if (statesize > 0 && viewStates != null) { sizenum--; toth -= statesize; } toth -= panePad*2*(viewCount-1); int cury = 0; for (i = 0; i != viewCount; i++) { View v = viewList[i]; int h = (sizenum == 0) ? toth : toth/sizenum; if (v == viewPotential && potsize > 0) h = potsize; else if (v == viewStates && statesize > 0) h = statesize; v.paneY = cury; if (cury > 0) cury += panePad; v.x = 0; v.width = winSize.width; v.y = cury; v.height = h; cury += h+panePad; } setSubViews(); } void setSubViews() { int i; pixels = null; if (useBufferedImage) { try { /* simulate the following code using reflection: dbimage = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_RGB); DataBuffer db = (DataBuffer)(((BufferedImage)memimage). getRaster().getDataBuffer()); DataBufferInt dbi = (DataBufferInt) db; pixels = dbi.getData(); */ Class biclass = Class.forName("java.awt.image.BufferedImage"); Class dbiclass = Class.forName("java.awt.image.DataBufferInt"); Class rasclass = Class.forName("java.awt.image.Raster"); Constructor cstr = biclass.getConstructor( new Class[] { int.class, int.class, int.class }); memimage = (Image) cstr.newInstance(new Object[] { new Integer(viewX.width), new Integer(viewX.height), new Integer(1)}); // BufferedImage.TYPE_INT_RGB)}); Method m = biclass.getMethod("getRaster", null); Object ras = m.invoke(memimage, null); Object db = rasclass.getMethod("getDataBuffer", null). invoke(ras, null); pixels = (int[]) dbiclass.getMethod("getData", null).invoke(db, null); } catch (Exception ee) { // ee.printStackTrace(); System.out.println("BufferedImage failed"); } } if (pixels == null) { pixels = new int[viewX.width*viewX.height]; for (i = 0; i != viewX.width*viewX.height; i++) pixels[i] = 0xFF000000; imageSource = new MemoryImageSource(viewX.width, viewX.height, pixels, 0, viewX.width); imageSource.setAnimated(true); imageSource.setFullBufferUpdates(true); memimage = cv.createImage(imageSource); } int asize = (int) (min(viewX.width, viewX.height)/3); viewAxes = new Rectangle(viewX.x+winSize.width-asize, viewX.y, asize, asize); viewField = new Rectangle(viewX.x+winSize.width-asize, viewX.y+viewX.height-asize, asize, asize); createPhasors(); } int getTermWidth() { return 8; } // multiply rotation matrix by rotations through angle1 and angle2 void rotate(double angle1, double angle2) { double r1cos = Math.cos(angle1); double r1sin = Math.sin(angle1); double r2cos = Math.cos(angle2); double r2sin = Math.sin(angle2); double rotm2[] = new double[9]; // angle1 is angle about y axis, angle2 is angle about x axis rotm2[0] = r1cos; rotm2[1] = -r1sin*r2sin; rotm2[2] = r2cos*r1sin; rotm2[3] = 0; rotm2[4] = r2cos; rotm2[5] = r2sin; rotm2[6] = -r1sin; rotm2[7] = -r1cos*r2sin; rotm2[8] = r1cos*r2cos; double rotm1[] = rotmatrix; rotmatrix = new double[9]; int i, j, k; for (j = 0; j != 3; j++) for (i = 0; i != 3; i++) { double v = 0; for (k = 0; k != 3; k++) v += rotm1[k+j*3]*rotm2[i+k*3]; rotmatrix[i+j*3] = v; } } double max(double a, double b) { return a > b ? a : b; } double min(double a, double b) { return a < b ? a : b; } void setResolution() { int og = gridSizeX; gridSizeX = gridSizeY = (resolutionBar.getValue() & ~1); if (og == gridSizeX) return; dataSize = gridSizeX*4; // (internalResBar.getValue() & ~1); System.out.print("setResolution " + dataSize + " " + gridSizeX + "\n"); // was 50 resadj = 50./dataSize; precomputeAll(); } // compute 2-d view by raytracing through a 3-d dataset void computeView(double normmult) { int i, j; double q = 3.14159265/dataSize; boolean color = colorCheck.getState(); for (i = 0; i != orbCount; i++) orbitals[i].setupFrame(normmult); double izoom = 1/zoom; double rotm[] = rotmatrix; double aratio = viewX.width/(double) viewX.height; double xmult = dataSize/2.; double ymult = dataSize/2.; double zmult = dataSize/2.; double aratiox = izoom, aratioy = izoom; // preserve aspect ratio no matter what window dimensions if (aratio < 1) aratioy /= aratio; else aratiox *= aratio; int slice = sliceChooser.getSelectedIndex(); double boundRadius2 = 0; for (i = 0; i != orbCount; i++) { Orbital oo = orbitals[i]; double br = oo.getBoundRadius(colorMult); if (br > boundRadius2) boundRadius2 = br; } boundRadius2 *= boundRadius2; for (i = 0; i != gridSizeX; i++) for (j = 0; j != gridSizeY; j++) { // calculate camera direction double camvx0 = (2*i/(double) gridSizeX - 1)*aratiox; double camvy0 = -(2*j/(double) gridSizeY - 1)*aratioy; // rotate camera with rotation matrix double camx = rotm[2]*viewDistance; double camy = rotm[5]*viewDistance; double camz = rotm[8]*viewDistance; double camvx = rotm[0]*camvx0+rotm[1]*camvy0-rotm[2]; double camvy = rotm[3]*camvx0+rotm[4]*camvy0-rotm[5]; double camvz = rotm[6]*camvx0+rotm[7]*camvy0-rotm[8]; double camnorm = Math.sqrt(camvx0*camvx0+camvy0*camvy0+1); int n; float simpr = 0; float simpg = 0; float simpb = 0; // calculate intersections with bounding sphere double a = camvx*camvx+camvy*camvy+camvz*camvz; double b = 2*(camvx*camx+camvy*camy+camvz*camz); double c = camx*camx+camy*camy+camz*camz-boundRadius2; double discrim = b*b-4*a*c; if (discrim < 0) { // doesn't hit it fillSquare(i, j, 0, 0, 0); continue; } discrim = Math.sqrt(discrim); double mint = (-b-discrim)/(2*a); double maxt = (-b+discrim)/(2*a); if (slice != SLICE_NONE) { double t = -100; switch (slice) { case SLICE_X: t = (sliceval-camx)/camvx; break; case SLICE_Y: t = (sliceval-camy)/camvy; break; case SLICE_Z: t = (sliceval-camz)/camvz; break; } if (t < mint || t > maxt) { fillSquare(i, j, 0, 0, 0); continue; } mint = maxt = t; } // sample evenly along intersecting portion double tstep = (maxt-mint)/(sampleCount-1); double pathlen = (maxt-mint)*camnorm; int maxn = sampleCount-1; n = 1; double xx = (camx + camvx * mint) * xmult; double yy = (camy + camvy * mint) * ymult; double zz = (camz + camvz * mint) * zmult; if (slice != SLICE_NONE) { maxn = 1; n = 0; pathlen = 2; if (xx > xmult || yy > ymult || zz > zmult || xx < -xmult || yy < -ymult || zz < -zmult) { fillSquare(i, j, 0, 0, 0); continue; } } camvx *= tstep*xmult; camvy *= tstep*ymult; camvz *= tstep*zmult; int dshalf = dataSize/2; int oi; for (; n < maxn; n++) { // find grid element that contains sampled point double r = Math.sqrt(xx*xx+yy*yy+zz*zz); double costh = zz/r; int ri = (int) r; int costhi = (int) (costh*dshalf+dshalf); float fr = 0, fi = 0; calcPhiComponent(xx, yy); for (oi = 0; oi != orbCount; oi++) { Orbital oo = orbitals[oi]; oo.computePoint(ri, costhi); fr += funcr; fi += funci; } if (color) { double fv = fr*fr+fi*fi; fv *= sampleMult[n]; PhaseColor col = getPhaseColor(fr, fi); simpr += col.r * fv; simpg += col.g * fv; simpb += col.b * fv; } else { float fv = (fr*fr+fi*fi) * sampleMult[n]; simpr = simpg = (simpb += fv); } xx += camvx; yy += camvy; zz += camvz; } simpr *= pathlen/n; simpg *= pathlen/n; simpb *= pathlen/n; fillSquare(i, j, simpr, simpg, simpb); } } void fillSquare(int i, int j, float cr, float cg, float cb) { int winw = viewX.width; int winh = viewX.height; int x = i*winw/gridSizeX; int y = j*winh/gridSizeY; int x2 = (i+1)*winw/gridSizeX; int y2 = (j+1)*winh/gridSizeY; cr *= colorMult; cg *= colorMult; cb *= colorMult; int k, l; if (cr == 0 && cg == 0 && cb == 0) { int y2l = y2*viewX.width; for (k = x; k < x2; k++) for (l = y*viewX.width; l < y2l; l += viewX.width) pixels[k+l] = 0xFF000000; return; } double fm = max(cr, max(cg, cb)); if (fm > 255) { fm /= 255; cr /= fm; cg /= fm; cb /= fm; } int colval = 0xFF000000 + (((int) cr) << 16) | (((int) cg) << 8) | (((int) cb)); int y2l = y2*viewX.width; for (k = x; k < x2; k++) for (l = y*viewX.width; l < y2l; l += viewX.width) pixels[k+l] = colval; } PhaseColor getPhaseColor(double x, double y) { double val = 0; if (x == 0 && y == 0) return phaseColors[0]; int offset = 0; if (y >= 0) { if (x >= 0) { if (x >= y) { offset = 0; val = y/x; } else { offset = phaseColorCount; val = 1-x/y; } } else { if (-x <= y) { offset = 2*phaseColorCount; val = -x/y; } else { offset = 3*phaseColorCount; val = 1+y/x; } } } else { if (x <= 0) { if (y >= x) { offset = 4*phaseColorCount; val = y/x; } else { offset = 5*phaseColorCount; val = 1-x/y; } } else { if (-y >= x) { offset = 6*phaseColorCount; val = -x/y; } else { offset = 7*phaseColorCount; val = 1+y/x; } } } return phaseColors[offset+(int) (val*(phaseColorCount-1))]; } void calcPhiComponent(double x, double y) { int phiSector = 0; double val = 0; if (x == 0 && y == 0) { phiIndex = 0; return; } if (y >= 0) { if (x >= 0) { if (x >= y) { phiSector = 0; val = y/x; } else { phiSector = 1; val = 1-x/y; } } else { if (-x <= y) { phiSector = 2; val = -x/y; } else { phiSector = 3; val = 1+y/x; } } } else { if (x <= 0) { if (y >= x) { phiSector = 4; val = y/x; } else { phiSector = 5; val = 1-x/y; } } else { if (-y >= x) { phiSector = 6; val = -x/y; } else { phiSector = 7; val = 1+y/x; } } } phiIndex = (phiSector*(dataSize+1))+(int) (val*dataSize); } double getScaleRadius(int n) { int l = 0; // set scale by solving equation Veff(r) = E, assuming m=0 // Veff(r) = -1/r + l(l+1)/2, E = 1/2n^2 double b0 = -n*n*2; double c0 = l*(l+1)*n*n; double r0 = .5*(-b0+Math.sqrt(b0*b0-4*c0)); return r0; } void setScale(int n) { if (manualScale || !autoZoomItem.getState()) return; double outer = getScaleRadius(n); // 3.15 is fudge factor determined by trial and error int scaleValue = (int) (outer*3.15); int oldScaleValue = scaleBar.getValue(); if (oldScaleValue != scaleValue) { int diff = scaleValue - oldScaleValue; if (diff < -5 || diff > 5) { diff /= 3; if (diff < -50) diff = -50; if (diff > 50) diff = 50; } int nv = oldScaleValue+diff; if (!animatedZoomItem.getState()) nv = scaleValue; scaleBar.setValue(nv); scaleValue = nv; precomputeAll(); } } void precomputeAll() { int i; for (i = 0; i != orbCount; i++) { Orbital orb = orbitals[i]; orb.precompute(); } } int sign(double x) { return x < 0 ? -1 : 1; } public void paint(Graphics g) { cv.repaint(); } long lastFrameTime; public void updateAtomTrans(Graphics realg) { int n1 = 1; int n2 = 1; switch (freqChooser.getSelectedIndex()) { case 0: n1 = 1; n2 = 2; break; case 1: n1 = 1; n2 = 3; break; case 2: n1 = 1; n2 = 4; break; case 3: n1 = 2; n2 = 3; break; case 4: n1 = 2; n2 = 4; break; case 5: n1 = 3; n2 = 4; break; }; Graphics g = null; if (winSize == null || winSize.width == 0) return; g = dbimage.getGraphics(); g.setColor(cv.getBackground()); g.fillRect(0, 0, winSize.width, winSize.height); g.setColor(cv.getForeground()); if (fontMetrics == null) fontMetrics = g.getFontMetrics(); boolean allQuiet = false; // XXX double norm = 0; double normmult = 0, normmult2 = 0; normalize(); int i; for (i = 0; i != stateCount; i++) { State st = states[i]; norm += st.magSquared(); } normmult2 = 1/norm; if (norm == 0) normmult2 = 0; normmult = Math.sqrt(normmult2); setScale(n2); //setBrightness(normmult2); boolean sliced = sliceChooser.getSelectedIndex() != SLICE_NONE; zoom = (sliced) ? 8 : 16.55; colorMult = Math.exp(brightnessBar.getValue()/100.); //System.out.println(colorMult); computeView(normmult); int j, k; for (i = 1; i != viewCount; i++) { g.setColor(i == selectedPaneHandle ? Color.yellow : Color.gray); g.drawLine(0, viewList[i].paneY, winSize.width, viewList[i].paneY); } if (viewPotential != null) { double ymult = viewPotential.height * 1.9; g.setColor(Color.darkGray); for (i = 1; i != 16; i++) { double e = -1/(2.*i*i); int y = viewPotential.y - (int) (ymult * e); g.drawLine(0, y, winSize.width, y); } double xp = getScaler(); g.setColor(Color.white); int ox = -1, oy = -1; int x; int floory = viewPotential.y + viewPotential.height - 1; for (x = 0; x != winSize.width; x++) { double xx = (x-winSize.width/2)*xp; if (xx < 0) xx = -xx; if (xx < 1e-3) xx = 1e-3; double dy = -1/xx; int y = viewPotential.y - (int) (ymult * dy); if (y > floory) { if (ox == -1) continue; g.drawLine(ox, oy, ox, floory); ox = -1; continue; } if (ox == -1 && x > 0) { g.drawLine(x, floory, x, y); ox = x; oy = y; continue; } if (ox != -1) g.drawLine(ox, oy, x, y); ox = x; oy = y; } // calculate expectation value of E if (norm != 0) { double expecte = 0; for (i = 0; i != stateCount; i++) { State st = states[i]; double prob = st.magSquared()*normmult2; expecte += prob*st.elevel; } int y = viewPotential.y - (int) (ymult * expecte); g.setColor(Color.red); g.drawLine(0, y, winSize.width, y); } if (selectedState != null && !dragging) { g.setColor(Color.yellow); int y = viewPotential.y - (int) (ymult * selectedState.elevel); g.drawLine(0, y, winSize.width, y); } } if (imageSource != null) imageSource.newPixels(); g.drawImage(memimage, viewX.x, viewX.y, null); g.setColor(Color.white); if (sliced) drawCube(g, false); if (axesItem.getState()) drawAxes(g); g.setColor(Color.yellow); if (selectedState != null) centerString(g, selectedState.getText(), viewX.y+viewX.height-5); else if (dimensionsItem.getState()) { double xp = getScaler(); double w = winSize.width*xp * 52.9463; double lambda = 91.1763/(1./(n1*n1)-1./(n2*n2))+.5; double period = 3.3356 * lambda; // calculate expectation value of E double expecte = 0; if (norm != 0) { for (i = 0; i != stateCount; i++) { State st = states[i]; double prob = st.magSquared()*normmult2; expecte += prob*st.elevel; } } double energy = 13.6*2*expecte; energy = ((int) (energy*10))/10.; centerString(g, "Screen width = " + (int)w + " pm, " + "Wavelength = " + (int)lambda + " nm", viewX.y+viewX.height-25); centerString(g, "Period = " + (int)period + " attoseconds, " + " = " + energy + " eV", viewX.y+viewX.height-5); } if (viewStates != null) { drawPhasors(g, viewStates); drawTransitions(g, viewStates, n1, n2); } long steprate = 24*speedBar.getValue(); boolean stopped = (stoppedCheck.getState() || dragging); if (stopped) lastFrameTime = 0; else allQuiet = false; // iteration if (newcoef == null) { newcoef = new Complex[stateCount]; for (j = 0; j != stateCount; j++) newcoef[j] = new Complex(); } double tadd = Math.exp(stepBar.getValue()/20.)*(.1/5); double strengthMul = Math.exp(strengthBar.getValue()/20.)/(9*455e2); freq = getState(n1, 0, 0).elevel - getState(n2, 0, 0).elevel; double dipoleArr[] = null; boolean circular = false; int cwmult = 1; switch (dirChooser.getSelectedIndex()) { case DIR_X: dipoleArr = dipoleOpX; break; case DIR_Y: dipoleArr = dipoleOpY; break; case DIR_Z: dipoleArr = dipoleOpZ; break; case DIR_CCW: circular = true; cwmult = -1; break; case DIR_CW: circular = true; break; } Complex mul = new Complex(); Complex newval = new Complex(); double baseEnergy = getState(n1, 0, 0).elevel-.01; int iter; for (iter = 1; ; iter++) { if (stopped) break; double mul1 = strengthMul * Math.sin(t*freq) * tadd; double mul2 = strengthMul * Math.cos(t*freq) * tadd; freqPhase = (t*freq) % (2*pi); for (j = 0; j != stateCount; j++) { BasisState st = states[j]; if (restrictItem.getState() && st.n != n1 && st.n != n2) { newcoef[j].set(0); continue; } newval.set(st); for (k = 0; k != stateCount; k++) { BasisState sk = states[k]; if (sk.n != n1 && sk.n != n2) continue; if (circular) { double dpx = dipoleOp(j, k, dipoleOpX); double dpy = -cwmult*dipoleOp(j, k, dipoleOpY); mul.set(mul2*dpx, mul1*dpy); } else { double dp = mul1*dipoleOp(j, k, dipoleArr); if (dipoleArr == dipoleOpY) mul.set(0, -dp); else mul.set(dp); } mul.multI(); mul.mult(sk); newval.addQuick(mul); } double ang = -(st.elevel-baseEnergy)*tadd; double angr = Math.cos(ang); double angi = Math.sin(ang); newval.mult(angr, angi); if (newval.mag > 1e-6) newcoef[j].set(newval); else newcoef[j].set(0); } for (j = 0; j != stateCount; j++) states[j].set(newcoef[j]); t += tadd; long tm = System.currentTimeMillis(); if (iter*1000 >= steprate*(tm-lastFrameTime) || (tm-lastFrameTime > 500)) break; } double ex = 0, ey = 0, ez = 0; switch (dirChooser.getSelectedIndex()) { case DIR_X: ex = Math.sin(t*freq); break; case DIR_Y: ey = Math.sin(t*freq); break; case DIR_Z: ez = Math.sin(t*freq); break; case DIR_CCW: case DIR_CW: ex = Math.cos(t*freq); ey = cwmult*Math.sin(t*freq); break; } normalize(); createOrbitals(); int q = (int) (20*Math.sin(t*freq)); g.setColor(Color.darkGray); double da = .5; map3d(0, 0, 0, xpoints, ypoints, 0, viewField); map3d(da, 0, 0, xpoints, ypoints, 1, viewField); g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]); map3d(0, da, 0, xpoints, ypoints, 1, viewField); g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]); map3d(0, 0, da, xpoints, ypoints, 1, viewField); g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]); g.setColor(Color.white); map3d(ex*da, ey*da, ez*da, xpoints, ypoints, 1, viewField); drawArrow(g, null, xpoints[0], ypoints[0], xpoints[1], ypoints[1]); if (!dimensionsItem.getState()) g.drawString("qE", viewField.x + (viewField.width-fontMetrics.stringWidth("qE"))/2, viewField.y+viewField.height-10); realg.drawImage(dbimage, 0, 0, this); lastFrameTime = System.currentTimeMillis(); if (!allQuiet) cv.repaint(pause); } double dipoleOpX[] = { 0., 0., 0., 0., 0., 0.526749, 0., -0.526749, 0.210938, 0., -0.210938, 0.124346, 0., -0.124346, 0.085203, 0., -0.085203, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -2.12132, 0., 2.12132, 1.25121, 0., -1.25121, 0.523487, 0., -0.523487, 0.315964, 0., -0.315964, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.383102, 0., -0.383102, -5.19615, 0., 5.19615, 2.23285, 0., -2.23285, 0.922468, 0., -0.922468, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.156074, 0., -0.156074, 0.997569, 0., -0.997569, -9.48683, 0., 9.48683, 3.47739, 0., -3.47739, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.0930919, 0., -0.0930919, 0.395842, 0., -0.395842, 1.87806, 0., -1.87806, -15., 0., 15., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.526749, -2.12132, 0.383102, 0.156074, 0.0930919, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 2.12337, 0., -0.866861, 0., 0., 0.764602, 0., -0.312148, 0., 0., 0.436072, 0., -0.178026, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.50145, 0., -1.50145, 0., 0., 0.540655, 0., -0.540655, 0., 0., 0.30835, 0., -0.30835, 0., -0.526749, 2.12132, -0.383102, -0.156074, -0.0930919, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.866861, 0., -2.12337, 0., 0., 0.312148, 0., -0.764602, 0., 0., 0.178026, 0., -0.436072, 0.210938, 1.25121, -5.19615, 0.997569, 0.395842, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -4.5, 0., 1.83712, 0., 0., 3.38335, 0., -1.38125, 0., 0., 1.32747, 0., -0.541939, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -3.18198, 0., 3.18198, 0., 0., 2.39239, 0., -2.39239, 0., 0., 0.938666, 0., -0.938666, 0., -0.210938, -1.25121, 5.19615, -0.997569, -0.395842, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -1.83712, 0., 4.5, 0., 0., 1.38125, 0., -3.38335, 0., 0., 0.541939, 0., -1.32747, 0.124346, 0.523487, 2.23285, -9.48683, 1.87806, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.582386, 0., -0.237758, 0., 0., -9.29516, 0., 3.79473, 0., 0., 4.93677, 0., -2.01543, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.411809, 0., -0.411809, 0., 0., -6.57267, 0., 6.57267, 0., 0., 3.49082, 0., -3.49082, 0., -0.124346, -0.523487, -2.23285, 9.48683, -1.87806, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.237758, 0., -0.582386, 0., 0., -3.79473, 0., 9.29516, 0., 0., 2.01543, 0., -4.93677, 0.085203, 0.315964, 0.922468, 3.47739, -15., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.215914, 0., -0.0881464, 0., 0., 1.36191, 0., -0.555997, 0., 0., -15.3704, 0., 6.27495, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.152674, 0., -0.152674, 0., 0., 0.963015, 0., -0.963015, 0., 0., -10.8685, 0., 10.8685, 0., -0.085203, -0.315964, -0.922468, -3.47739, 15., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.0881464, 0., -0.215914, 0., 0., 0.555997, 0., -1.36191, 0., 0., -6.27495, 0., 15.3704, 0., 0., 0., 0., 0., 2.12337, 0., 0., -4.5, 0., 0., 0.582386, 0., 0., 0.215914, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.50145, 0., 0., -3.18198, 0., 0., 0.411809, 0., 0., 0.152674, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.866861, 0., 0.866861, 1.83712, 0., -1.83712, -0.237758, 0., 0.237758, -0.0881464, 0., 0.0881464, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -1.50145, 0., 0., 3.18198, 0., 0., -0.411809, 0., 0., -0.152674, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -2.12337, 0., 0., 4.5, 0., 0., -0.582386, 0., 0., -0.215914, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.764602, 0., 0., 3.38335, 0., 0., -9.29516, 0., 0., 1.36191, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.540655, 0., 0., 2.39239, 0., 0., -6.57267, 0., 0., 0.963015, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.312148, 0., 0.312148, -1.38125, 0., 1.38125, 3.79473, 0., -3.79473, -0.555997, 0., 0.555997, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.540655, 0., 0., -2.39239, 0., 0., 6.57267, 0., 0., -0.963015, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.764602, 0., 0., -3.38335, 0., 0., 9.29516, 0., 0., -1.36191, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.436072, 0., 0., 1.32747, 0., 0., 4.93677, 0., 0., -15.3704, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.30835, 0., 0., 0.938666, 0., 0., 3.49082, 0., 0., -10.8685, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.178026, 0., 0.178026, -0.541939, 0., 0.541939, -2.01543, 0., 2.01543, 6.27495, 0., -6.27495, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.30835, 0., 0., -0.938666, 0., 0., -3.49082, 0., 0., 10.8685, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.436072, 0., 0., -1.32747, 0., 0., -4.93677, 0., 0., 15.3704, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.}; double dipoleOpY[] = { 0., 0., 0., 0., 0., 0.526749, 0., 0.526749, 0.210938, 0., 0.210938, 0.124346, 0., 0.124346, 0.085203, 0., 0.085203, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -2.12132, 0., -2.12132, 1.25121, 0., 1.25121, 0.523487, 0., 0.523487, 0.315964, 0., 0.315964, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.383102, 0., 0.383102, -5.19615, 0., -5.19615, 2.23285, 0., 2.23285, 0.922468, 0., 0.922468, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.156074, 0., 0.156074, 0.997569, 0., 0.997569, -9.48683, 0., -9.48683, 3.47739, 0., 3.47739, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.0930919, 0., 0.0930919, 0.395842, 0., 0.395842, 1.87806, 0., 1.87806, -15., 0., -15., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.526749, 2.12132, -0.383102, -0.156074, -0.0930919, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 2.12337, 0., 0.866861, 0., 0., 0.764602, 0., 0.312148, 0., 0., 0.436072, 0., 0.178026, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.50145, 0., 1.50145, 0., 0., 0.540655, 0., 0.540655, 0., 0., 0.30835, 0., 0.30835, 0., -0.526749, 2.12132, -0.383102, -0.156074, -0.0930919, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.866861, 0., 2.12337, 0., 0., 0.312148, 0., 0.764602, 0., 0., 0.178026, 0., 0.436072, -0.210938, -1.25121, 5.19615, -0.997569, -0.395842, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -4.5, 0., -1.83712, 0., 0., 3.38335, 0., 1.38125, 0., 0., 1.32747, 0., 0.541939, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -3.18198, 0., -3.18198, 0., 0., 2.39239, 0., 2.39239, 0., 0., 0.938666, 0., 0.938666, 0., -0.210938, -1.25121, 5.19615, -0.997569, -0.395842, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -1.83712, 0., -4.5, 0., 0., 1.38125, 0., 3.38335, 0., 0., 0.541939, 0., 1.32747, -0.124346, -0.523487, -2.23285, 9.48683, -1.87806, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.582386, 0., 0.237758, 0., 0., -9.29516, 0., -3.79473, 0., 0., 4.93677, 0., 2.01543, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.411809, 0., 0.411809, 0., 0., -6.57267, 0., -6.57267, 0., 0., 3.49082, 0., 3.49082, 0., -0.124346, -0.523487, -2.23285, 9.48683, -1.87806, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.237758, 0., 0.582386, 0., 0., -3.79473, 0., -9.29516, 0., 0., 2.01543, 0., 4.93677, -0.085203, -0.315964, -0.922468, -3.47739, 15., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.215914, 0., 0.0881464, 0., 0., 1.36191, 0., 0.555997, 0., 0., -15.3704, 0., -6.27495, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.152674, 0., 0.152674, 0., 0., 0.963015, 0., 0.963015, 0., 0., -10.8685, 0., -10.8685, 0., -0.085203, -0.315964, -0.922468, -3.47739, 15., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.0881464, 0., 0.215914, 0., 0., 0.555997, 0., 1.36191, 0., 0., -6.27495, 0., -15.3704, 0., 0., 0., 0., 0., -2.12337, 0., 0., 4.5, 0., 0., -0.582386, 0., 0., -0.215914, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -1.50145, 0., 0., 3.18198, 0., 0., -0.411809, 0., 0., -0.152674, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.866861, 0., -0.866861, 1.83712, 0., 1.83712, -0.237758, 0., -0.237758, -0.0881464, 0., -0.0881464, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -1.50145, 0., 0., 3.18198, 0., 0., -0.411809, 0., 0., -0.152674, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -2.12337, 0., 0., 4.5, 0., 0., -0.582386, 0., 0., -0.215914, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.764602, 0., 0., -3.38335, 0., 0., 9.29516, 0., 0., -1.36191, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.540655, 0., 0., -2.39239, 0., 0., 6.57267, 0., 0., -0.963015, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.312148, 0., -0.312148, -1.38125, 0., -1.38125, 3.79473, 0., 3.79473, -0.555997, 0., -0.555997, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.540655, 0., 0., -2.39239, 0., 0., 6.57267, 0., 0., -0.963015, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.764602, 0., 0., -3.38335, 0., 0., 9.29516, 0., 0., -1.36191, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.436072, 0., 0., -1.32747, 0., 0., -4.93677, 0., 0., 15.3704, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.30835, 0., 0., -0.938666, 0., 0., -3.49082, 0., 0., 10.8685, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.178026, 0., -0.178026, -0.541939, 0., -0.541939, -2.01543, 0., -2.01543, 6.27495, 0., 6.27495, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.30835, 0., 0., -0.938666, 0., 0., -3.49082, 0., 0., 10.8685, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -0.436072, 0., 0., -1.32747, 0., 0., -4.93677, 0., 0., 15.3704, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0. }; double dipoleOpZ[] = { 0., 0., 0., 0., 0., 0., 0.744936, 0., 0., 0.298311, 0., 0., 0.175852, 0., 0., 0.120495, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -3., 0., 0., 1.76947, 0., 0., 0.740323, 0., 0., 0.446841, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.541788, 0., 0., -7.34847, 0., 0., 3.15772, 0., 0., 1.30457, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.220722, 0., 0., 1.41077, 0., 0., -13.4164, 0., 0., 4.91777, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.131652, 0., 0., 0.559805, 0., 0., 2.65597, 0., 0., -21.2132, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 2.12337, 0., 0., 0., 0., 0.764602, 0., 0., 0., 0., 0.436072, 0., 0., 0., 0.744936, -3., 0.541788, 0.220722, 0.131652, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 2.45185, 0., 0., 0., 0., 0.882887, 0., 0., 0., 0., 0.503533, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 2.12337, 0., 0., 0., 0., 0.764602, 0., 0., 0., 0., 0.436072, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -4.5, 0., 0., 0., 0., 3.38335, 0., 0., 0., 0., 1.32747, 0., 0., 0., 0.298311, 1.76947, -7.34847, 1.41077, 0.559805, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -5.19615, 0., 0., 0., 0., 3.90676, 0., 0., 0., 0., 1.53283, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -4.5, 0., 0., 0., 0., 3.38335, 0., 0., 0., 0., 1.32747, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.582386, 0., 0., 0., 0., -9.29516, 0., 0., 0., 0., 4.93677, 0., 0., 0., 0.175852, 0.740323, 3.15772, -13.4164, 2.65597, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.672481, 0., 0., 0., 0., -10.7331, 0., 0., 0., 0., 5.70049, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.582386, 0., 0., 0., 0., -9.29516, 0., 0., 0., 0., 4.93677, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.215914, 0., 0., 0., 0., 1.36191, 0., 0., 0., 0., -15.3704, 0., 0., 0., 0.120495, 0.446841, 1.30457, 4.91777, -21.2132, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.249316, 0., 0., 0., 0., 1.5726, 0., 0., 0., 0., -17.7482, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.215914, 0., 0., 0., 0., 1.36191, 0., 0., 0., 0., -15.3704, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 2.12337, 0., 0., -4.5, 0., 0., 0.582386, 0., 0., 0.215914, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 2.45185, 0., 0., -5.19615, 0., 0., 0.672481, 0., 0., 0.249316, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 2.12337, 0., 0., -4.5, 0., 0., 0.582386, 0., 0., 0.215914, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.764602, 0., 0., 3.38335, 0., 0., -9.29516, 0., 0., 1.36191, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.882887, 0., 0., 3.90676, 0., 0., -10.7331, 0., 0., 1.5726, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.764602, 0., 0., 3.38335, 0., 0., -9.29516, 0., 0., 1.36191, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.436072, 0., 0., 1.32747, 0., 0., 4.93677, 0., 0., -15.3704, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.503533, 0., 0., 1.53283, 0., 0., 5.70049, 0., 0., -17.7482, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.436072, 0., 0., 1.32747, 0., 0., 4.93677, 0., 0., -15.3704, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0. }; double dipoleOp(int a, int b, double arr[]) { BasisState sa = states[a]; BasisState sb = states[b]; if (sa.n == sb.n) return 0; if (sa.l > 2 || sb.l > 2) return 0; if (sa.n > 5 || sb.n > 5) return 0; int san = getDipoleStateNum(sa); int sbn = getDipoleStateNum(sb); return arr[san*32+sbn]; } int getDipoleStateNum(BasisState bs) { if (bs.l == 0) return bs.n-1; if (bs.l == 1) return 5 + (bs.n-2)*3 + bs.m + 1; return 17 + (bs.n-3)*5 + bs.m + 2; } double getScaler() { // XXX don't duplicate this double scalex = viewX.width*zoom/2; double scaley = viewX.height*zoom/2; double aratio = viewX.width/(double) viewX.height; // preserve aspect ratio regardless of window dimensions if (aratio < 1) scaley *= aratio; else scalex /= aratio; double xp = 2*scalex/viewDistance; double mult = scaleBar.getValue() / 50.; xp /= 50*mult; xp = 1/xp; return xp; } public void centerString(Graphics g, String str, int ypos) { g.drawString(str, (winSize.width-fontMetrics.stringWidth(str))/2, ypos); } // see if the face containing (nx, ny, nz) is visible. boolean visibleFace(int nx, int ny, int nz) { double viewx = viewDistance*rotmatrix[2]; double viewy = viewDistance*rotmatrix[5]; double viewz = viewDistance*rotmatrix[8]; return (nx-viewx)*nx+(ny-viewy)*ny+(nz-viewz)*nz < 0; } void drawPhasors(Graphics g, View v) { int i; for (i = 0; i != phasorCount; i++) { Phasor ph = phasors[i]; State st = ph.state; int ss = ph.width; int ss2 = ss/2; int x = ph.x + ss2; int y = ph.y + ss2; boolean yel = (selectedState == st); g.setColor(yel ? Color.yellow : st.mag == 0 ? gray2 : Color.white); g.drawOval(x-ss2, y-ss2, ss, ss); int xa = (int) (st.re*ss2); int ya = (int) (-st.im*ss2); g.drawLine(x, y, x+xa, y+ya); g.drawLine(x+xa-1, y+ya, x+xa+1, y+ya); g.drawLine(x+xa, y+ya-1, x+xa, y+ya+1); } } void drawTransitions(Graphics g, View v, int n1, int n2) { int i; int offx = winSize.width/2; if (phasors[0].width*11 >= offx) return; Complex mul = new Complex(); Complex newval = new Complex(); boolean circular = false; int cwmult = 1; double dipoleArr[] = null; switch (dirChooser.getSelectedIndex()) { case DIR_X: dipoleArr = dipoleOpX; break; case DIR_Y: dipoleArr = dipoleOpY; break; case DIR_Z: dipoleArr = dipoleOpZ; break; case DIR_CCW: circular = true; cwmult = -1; break; case DIR_CW: circular = true; break; } for (i = 0; i != phasorCount; i++) { Phasor ph = phasors[i]; BasisState st = (BasisState) ph.state; int ss = ph.width; int ss2 = ss/2; int x = ph.x + ss2 + offx; int y = ph.y + ss2; boolean yel = (selectedState == st); g.setColor(yel ? Color.yellow : st.mag == 0 ? gray2 : Color.white); double dip = 0; int j; newval.set(0); for (j = 0; j != stateCount; j++) { BasisState sj = states[j]; int ii = st.index; if (circular) { double dpx = dipoleOp(ii, j, dipoleOpX); double dpy = -cwmult*dipoleOp(ii, j, dipoleOpY); mul.set(dpx+dpy); } else { double dp = dipoleOp(ii, j, dipoleArr); if (dipoleArr == dipoleOpY) mul.set(0, -dp); else mul.set(dp); } mul.multI(); mul.mult(sj); newval.addQuick(mul); } double nvn = (newval.re*newval.re + newval.im*newval.im)*1e4 + 1e-8; nvn = Math.sqrt(nvn); int c; // = (int) (6+Math.log(nvn)*16); c = (int) (nvn*3.5); if (c > 255) c = 255; if (c < 0) c = 0; g.setColor(new Color(0, 0, c)); g.fillOval(x-ss2, y-ss2, ss, ss); g.setColor(st.n == n1 || st.n == n2 ? purple : gray2); g.drawOval(x-ss2, y-ss2, ss, ss); } } void drawFunction(Graphics g, View view, int pos, double fr[], int count, int pad, boolean fromZero) { int i; double expectx = 0; double expectx2 = 0; double maxsq = 0; double tot = 0; int vw = winSize.width/3; int vw2 = vw*4/5; int mid_x = (fromZero) ? (vw2/(count-1)) : vw2 * (count/2) / (count-1); int zero = mid_x; mid_x += vw*pos; for (i = 0; i != count; i++) { int x = vw2 * i / (count-1); int ii = i; double dr = fr[ii]; double dy = dr*dr; if (dy > maxsq) maxsq = dy; int dev = x-zero; expectx += dy*dev; expectx2 += dy*dev*dev; tot += dy; } zero = mid_x; expectx /= tot; expectx2 /= tot; double maxnm = Math.sqrt(maxsq); double uncert = Math.sqrt(expectx2-expectx*expectx); int ox = -1, oy = 0; double bestscale = 1/maxnm; view.scale = bestscale; if (view.scale > 1e8) view.scale = 1e8; g.setColor(Color.gray); g.drawLine(mid_x, view.y, mid_x, view.y+view.height); double ymult2 = .90*view.height; int mid_y = view.y+view.height/2+(int) ymult2/2; double mult = ymult2*view.scale; g.setColor(Color.white); ox = -1; for (i = 0; i != count; i++) { int x = vw2 * i / (count-1) + vw*pos; int ii = i; int y = mid_y - (int) (mult * fr[ii]); if ((i % pad) == 1) { g.setColor(Color.gray); g.drawLine(x, mid_y, x, mid_y+4); g.setColor(Color.white); } if (ox != -1) g.drawLine(ox, oy, x, y); ox = x; oy = y; } if (maxsq > 0) { expectx += zero + .5; g.setColor(Color.red); g.drawLine((int) expectx, view.y, (int) expectx, view.y+view.height); } } // draw the cube containing the particles. if drawAll is false then // we just draw faces that are facing the camera. This routine draws // each edge twice which is unnecessary, but easier. void drawCube(Graphics g, boolean drawAll) { int i; int slice = sliceChooser.getSelectedIndex(); int sp = 0; for (i = 0; i != 6; i++) { // calculate normal of ith face int nx = (i == 0) ? -1 : (i == 1) ? 1 : 0; int ny = (i == 2) ? -1 : (i == 3) ? 1 : 0; int nz = (i == 4) ? -1 : (i == 5) ? 1 : 0; // if face is not facing camera, don't draw it if (!drawAll && !visibleFace(nx, ny, nz)) continue; double pts[]; pts = new double[3]; int n; for (n = 0; n != 4; n++) { computeFace(i, n, pts); map3d(pts[0], pts[1], pts[2], xpoints, ypoints, n, viewX); } g.setColor(Color.gray); g.drawPolygon(xpoints, ypoints, 4); if (slice != SLICE_NONE && i/2 != slice-SLICE_X) { if (selectedSlice) g.setColor(Color.yellow); int coord1 = (slice == SLICE_X) ? 1 : 0; int coord2 = (slice == SLICE_Z) ? 1 : 2; computeFace(i, 0, pts); pts[slice-SLICE_X] = sliceval; map3d(pts[0], pts[1], pts[2], slicerPoints[0], slicerPoints[1], sp, viewX); computeFace(i, 2, pts); pts[slice-SLICE_X] = sliceval; map3d(pts[0], pts[1], pts[2], slicerPoints[0], slicerPoints[1], sp+1, viewX); g.drawLine(slicerPoints[0][sp ], slicerPoints[1][sp], slicerPoints[0][sp+1], slicerPoints[1][sp+1]); sliceFaces[sp/2][0] = nx; sliceFaces[sp/2][1] = ny; sliceFaces[sp/2][2] = nz; sp += 2; } } sliceFaceCount = sp; } // generate the nth vertex of the bth cube face void computeFace(int b, int n, double pts[]) { // One of the 3 coordinates (determined by a) is constant. // When b=0, x=-1; b=1, x=+1; b=2, y=-1; b=3, y=+1; etc int a = b >> 1; pts[a] = ((b & 1) == 0) ? -1 : 1; // fill in the other 2 coordinates with one of the following // (depending on n): -1,-1; +1,-1; +1,+1; -1,+1 int i; for (i = 0; i != 3; i++) { if (i == a) continue; pts[i] = (((n>>1)^(n&1)) == 0) ? -1 : 1; n >>= 1; } } void drawAxes(Graphics g) { g.setColor(Color.white); double d = .5; map3d(0, 0, 0, xpoints, ypoints, 0, viewAxes); map3d(d, 0, 0, xpoints, ypoints, 1, viewAxes); drawArrow(g, "x", xpoints[0], ypoints[0], xpoints[1], ypoints[1]); map3d(0, d, 0, xpoints, ypoints, 1, viewAxes); drawArrow(g, "y", xpoints[0], ypoints[0], xpoints[1], ypoints[1]); map3d(0, 0, d, xpoints, ypoints, 1, viewAxes); drawArrow(g, "z", xpoints[0], ypoints[0], xpoints[1], ypoints[1]); } void drawArrow(Graphics g, String text, int x1, int y1, int x2, int y2) { drawArrow(g, text, x1, y1, x2, y2, 5); } void drawArrow(Graphics g, String text, int x1, int y1, int x2, int y2, int as) { g.drawLine(x1, y1, x2, y2); double l = Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); if (l > as/2) { // was as double hatx = (x2-x1)/l; double haty = (y2-y1)/l; g.drawLine(x2, y2, (int) (haty*as-hatx*as+x2), (int) (-hatx*as-haty*as+y2)); g.drawLine(x2, y2, (int) (-haty*as-hatx*as+x2), (int) (hatx*as-haty*as+y2)); if (text != null) g.drawString(text, (int) (x2+hatx*10), (int) (y2+haty*10)); } } // map 3-d point (x,y,z) to screen, storing coordinates // in xpoints[pt],ypoints[pt] void map3d(double x, double y, double z, int xpoints[], int ypoints[], int pt, Rectangle v) { double rotm[] = rotmatrix; double realx = x*rotm[0] + y*rotm[3] + z*rotm[6]; double realy = x*rotm[1] + y*rotm[4] + z*rotm[7]; double realz = viewDistance-(x*rotm[2] + y*rotm[5] + z*rotm[8]); double scalex = v.width*zoom/2; double scaley = v.height*zoom/2; double aratio = v.width/(double) v.height; // preserve aspect ratio regardless of window dimensions if (aratio < 1) scaley *= aratio; else scalex /= aratio; xpoints[pt] = v.x + v.width /2 + (int) (scalex*realx/realz); ypoints[pt] = v.y + v.height/2 - (int) (scaley*realy/realz); } // map point on screen to 3-d coordinates assuming it lies on a given plane void unmap3d(double x3[], int x, int y, double pn[], double pp[]) { // first, find all points which map to (x,y) on the screen. // this is a line. double scalex = viewX.width*zoom/2; double scaley = viewX.height*zoom/2; double aratio = viewX.width/(double) viewX.height; // preserve aspect ratio regardless of window dimensions if (aratio < 1) scaley *= aratio; else scalex /= aratio; double vx = (x-(viewX.x+viewX.width/2))/scalex; double vy = -(y-(viewX.y+viewX.height/2))/scaley; // vz = -1 // map the line vector to object space double rotm[] = rotmatrix; double mx = viewDistance*rotm[2]; double my = viewDistance*rotm[5]; double mz = viewDistance*rotm[8]; double mvx = (vx*rotm[0] + vy*rotm[1] - rotm[2]); double mvy = (vx*rotm[3] + vy*rotm[4] - rotm[5]); double mvz = (vx*rotm[6] + vy*rotm[7] - rotm[8]); // calculate the intersection between the line and the given plane double t = ((pp[0]-mx)*pn[0] + (pp[1]-my)*pn[1] + (pp[2]-mz)*pn[2]) / (pn[0]*mvx+pn[1]*mvy+pn[2]*mvz); x3[0] = mx+mvx*t; x3[1] = my+mvy*t; x3[2] = mz+mvz*t; } public void componentHidden(ComponentEvent e){} public void componentMoved(ComponentEvent e){} public void componentShown(ComponentEvent e) { cv.repaint(); } public void componentResized(ComponentEvent e) { handleResize(); cv.repaint(pause); } public void actionPerformed(ActionEvent e) { if (e.getSource() == exitItem) { applet.destroyFrame(); return; } cv.repaint(); } int scaleValue = -1; public void adjustmentValueChanged(AdjustmentEvent e) { //System.out.print(((Scrollbar) e.getSource()).getValue() + "\n"); if (e.getSource() == scaleBar) { if (scaleBar.getValue() == scaleValue) return; scaleValue = scaleBar.getValue(); precomputeAll(); manualScale = true; } if (e.getSource() == brightnessBar) { double mult = Math.exp(brightnessBar.getValue()/100.); userBrightMult = mult/bestBrightness; } if (e.getSource() == resolutionBar) setResolution(); setupSimpson(); cv.repaint(pause); } public void mouseDragged(MouseEvent e) { dragging = true; edit(e); dragX = e.getX(); dragY = e.getY(); } boolean csInRange(int x, int xa, int xb) { if (xa < xb) return x >= xa-5 && x <= xb+5; return x >= xb-5 && x <= xa+5; } void checkSlice(int x, int y) { if (sliceChooser.getSelectedIndex() == SLICE_NONE) { selectedSlice = false; return; } int n; selectedSlice = false; for (n = 0; n != sliceFaceCount; n += 2) { int xa = slicerPoints[0][n]; int xb = slicerPoints[0][n+1]; int ya = slicerPoints[1][n]; int yb = slicerPoints[1][n+1]; if (!csInRange(x, xa, xb) || !csInRange(y, ya, yb)) continue; double d; if (xa == xb) d = Math.abs(x-xa); else { // write line as y=a+bx double b = (yb-ya)/(double) (xb-xa); double a = ya-b*xa; // solve for distance double d1 = y-(a+b*x); if (d1 < 0) d1 = -d1; d = d1/Math.sqrt(1+b*b); } if (d < 6) { selectedSlice = true; sliceFace = sliceFaces[n/2]; break; } } } public void mouseMoved(MouseEvent e) { if (dragging) return; int x = e.getX(); int y = e.getY(); dragX = x; dragY = y; int oldsph = selectedPaneHandle; int olds = selection; State oldss = selectedState; selectedPaneHandle = -1; selection = 0; selectedState = null; int i; for (i = 1; i != viewCount; i++) { int dy = y-viewList[i].paneY; if (dy >= -3 && dy <= 3) { selectedPaneHandle = i; selection = SEL_HANDLE; } } if (viewX != null && viewX.inside(x, y)) { selection = SEL_X; checkSlice(e.getX(), e.getY()); } else if (viewPotential.contains(x, y)) { selection = SEL_POTENTIAL; //findStateByEnergy(y); } else if (viewStates != null && viewStates.inside(x, y)) findPhasor(viewStates, x, y); if (oldsph != selectedPaneHandle || olds != selection || oldss != selectedState) cv.repaint(pause); } void findPhasor(View v, int x, int y) { int i; for (i = 0; i != phasorCount; i++) { if (!phasors[i].inside(x, y)) continue; selectedPhasor = phasors[i]; selectedState = selectedPhasor.state; selection = SEL_STATES; break; } } public void mouseClicked(MouseEvent e) { if (selection == SEL_STATES) editMagClick(); if (e.getClickCount() == 2 && selectedState != null) enterSelectedState(); } void enterSelectedState() { int i; for (i = 0; i != stateCount; i++) if (states[i] != selectedState) states[i].set(0); selectedState.convertBasisToDerived(); selectedState.set(1); selectedState.convertDerivedToBasis(); createOrbitals(); cv.repaint(pause); } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { if (!dragging && selection != 0) { selectedPaneHandle = -1; selectedState = null; selectedPhasor = null; selection = 0; cv.repaint(pause); } } public void mousePressed(MouseEvent e) { if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == 0) return; dragX = dragStartX = e.getX(); dragY = dragStartY = e.getY(); dragZoomStart = zoom; dragging = true; edit(e); } public void mouseReleased(MouseEvent e) { if (dragging) cv.repaint(); dragging = false; } public void itemStateChanged(ItemEvent e) { if (e.getItemSelectable() instanceof CheckboxMenuItem) { setupDisplay(); cv.repaint(pause); return; } if (e.getItemSelectable() == setupChooser) doSetup(); cv.repaint(pause); } public boolean handleEvent(Event ev) { if (ev.id == Event.WINDOW_DESTROY) { destroyFrame(); return true; } return super.handleEvent(ev); } void destroyFrame() { if (applet == null) dispose(); else applet.destroyFrame(); } void edit(MouseEvent e) { if (selection == SEL_NONE) return; int x = e.getX(); int y = e.getY(); switch (selection) { case SEL_HANDLE: editHandle(y); break; case SEL_STATES: editMag(x, y); break; case SEL_POTENTIAL: break; case SEL_X: editX(x, y); break; } } void editHandle(int y) { int dy = y-viewList[selectedPaneHandle].paneY; View upper = viewList[selectedPaneHandle-1]; View lower = viewList[selectedPaneHandle]; int minheight = 10; if (upper.height+dy < minheight || lower.height-dy < minheight) return; upper.height += dy; lower.height -= dy; lower.y += dy; lower.paneY += dy; cv.repaint(pause); setSubViews(); } void editX(int x, int y) { int mode = MODE_ANGLE; if (selectedSlice) mode = MODE_SLICE; if (mode == MODE_ANGLE) { int xo = dragX-x; int yo = dragY-y; rotate(xo/40., -yo/40.); cv.repaint(pause); } else if (mode == MODE_SLICE) { double x3[] = new double[3]; unmap3d(x3, x, y, sliceFace, sliceFace); switch (sliceChooser.getSelectedIndex()) { case SLICE_X: sliceval = x3[0]; break; case SLICE_Y: sliceval = x3[1]; break; case SLICE_Z: sliceval = x3[2]; break; } if (sliceval < -.99) sliceval = -.99; if (sliceval > .99) sliceval = .99; cv.repaint(pause); } } void editMag(int x, int y) { if (selectedPhasor == null) return; int stateSize = selectedPhasor.width; int ss2 = stateSize/2; int x0 = selectedPhasor.x + ss2; int y0 = selectedPhasor.y + ss2; x -= x0; y -= y0; double mag = Math.sqrt(x*x+y*y)/ss2; double ang = Math.atan2(-y, x); if (mag > 10) mag = 0; if (mag > 1) mag = 1; selectedState.setMagPhase(mag, ang); cv.repaint(pause); createOrbitals(); } void editMagClick() { if (selectedState == null) return; if (magDragStart < .5) selectedState.set(1, 0); else selectedState.set(0); cv.repaint(pause); createOrbitals(); } void createOrbitals() { int i; int newOrbCount = 0; boolean newOrbitals = false; double magmin = 0; for (i = 0; i != stateCount; i++) { BasisState st = states[i]; if (st.m == 0) { if (st.mag > magmin) { newOrbCount++; if (st.orb == null) newOrbitals = true; } else if (st.orb != null) newOrbitals = true; } else if (st.m > 0) { if (st.mag > magmin || getState(st.n, st.l, -st.m).mag > magmin) { newOrbCount++; if (st.orb == null) newOrbitals = true; } else if (st.orb != null) newOrbitals = true; } } if (!newOrbitals) return; orbCount = newOrbCount; orbitals = new Orbital[orbCount]; int oi = 0; for (i = 0; i != stateCount; i++) { BasisState st = states[i]; if ((st.m == 0 && st.mag > magmin) || (st.m > 0 && (st.mag > magmin || getState(st.n, st.l, -st.m).mag > magmin))) { if (st.orb == null) { Orbital orb; if (st.l == 0) orb = new SOrbital(st); else if (st.m == 0) orb = new MZeroOrbital(st); else orb = new PairedOrbital(st); orb.precompute(); st.orb = orb; } orbitals[oi++] = st.orb; } else st.orb = null; } } void doClear() { int x; for (x = 0; x != stateCount; x++) states[x].set(0); } void normalize() { double norm = 0; int i; for (i = 0; i != stateCount; i++) norm += states[i].magSquared(); if (norm == 0) return; double normmult = 1/Math.sqrt(norm); for (i = 0; i != stateCount; i++) states[i].mult(normmult); cv.repaint(pause); } void maximize() { int i; double maxm = 0; for (i = 0; i != stateCount; i++) if (states[i].mag > maxm) maxm = states[i].mag; if (maxm == 0) return; for (i = 0; i != stateCount; i++) states[i].mult(1/maxm); cv.repaint(pause); } BasisState getState(int n, int l, int m) { int pre_n = n-1; int pre_n_add = pre_n*(pre_n+1)*(2*pre_n+1)/6; int pre_l_add = l*l; return states[pre_n_add+pre_l_add+l+m]; } int getStateIndex(int n, int l, int m) { int pre_n = n-1; int pre_n_add = pre_n*(pre_n+1)*(2*pre_n+1)/6; int pre_l_add = l*l; return pre_n_add+pre_l_add+l+m; } void setBrightness(double normmult) { int i; double avg = 0; double totn = 0; double minavg = 1e30; for (i = 0; i != orbCount; i++) { Orbital orb = orbitals[i]; double as = orb.getBrightness(); if (as < minavg) minavg = as; BasisState st = orb.state; double n = st.magSquared()*normmult; if (orb.state.m != 0) n += getState(st.n, st.l, -st.m).magSquared()*normmult; totn += n; avg += n*as; } bestBrightness = 113.9/(Math.sqrt(minavg)*totn); double mult = bestBrightness * userBrightMult; int bvalue = (int) (Math.log(mult)*100.); brightnessBar.setValue(bvalue); } abstract class Orbital { BasisState state; int n, l, m; float reMult, imMult; Orbital(BasisState bs) { n = bs.n; l = bs.l; m = bs.m; state = bs; } void setupFrame(double mult) { reMult = (float) (state.re*mult); imMult = (float) (state.im*mult); } float dataR[], dataTh[], dataPhiR[], dataPhiI[]; int dshalf; double brightnessCache; double getBoundRadius(double bright) { int i; int outer = 1; /* double maxThData = 0; if (l == 0) maxThData = 1; else { for (i = 0; i != dataSize; i++) { if (dataTh[i] > maxThData) maxThData = dataTh[i]; if (dataTh[i] < -maxThData) maxThData = -dataTh[i]; } }*/ // we need to divide the spherical harmonic norm out of // dataR[] to get just the radial function. (The spherical // norm gets multiplied into dataR[] for efficiency.) int mpos = (m < 0) ? -m : m; double norm1 = 1/sphericalNorm(l, mpos); //norm1 *= maxThData; norm1 *= norm1; norm1 *= bright; for (i = 0; i != dataSize; i++) { // XXX double v = dataR[i]*dataR[i]*norm1; if (v > 32) outer = i; } //System.out.println(maxThData + " " + outer); return outer / (dataSize/2.); } final int distmult = 4; void precompute() { int x, y, z; dshalf = dataSize/2; double mult = scaleBar.getValue() / 50.; int mpos = (m < 0) ? -m : m; double lgcorrect = Math.pow(-1, m); double norm = radialNorm(n, l)*sphericalNorm(l, mpos); dataR = new float[dataSize]; for (x = 0; x != dataSize; x++) { double r = x*resadj + .00000001; double rho = 2*r*mult/n; double rhol = Math.pow(rho, l)*norm; dataR[x] = (float) (hypser(l+1-n, 2*l+2, rho)*rhol* Math.exp(-rho/2)); } if (l > 0) { dataTh = new float[dataSize+1]; for (x = 0; x != dataSize+1; x++) { double th = (x-dshalf)/(double) dshalf; // we multiply in lgcorrect because plgndr() uses a // different sign convention than Bransden dataTh[x] = (float) (lgcorrect*plgndr(l, mpos, th)); } } if (m != 0) { dataPhiR = new float[8*(dataSize+1)]; dataPhiI = new float[8*(dataSize+1)]; int ix = 0; for (x = 0; x != 8; x++) for (y = 0; y <= dataSize; y++, ix++) { double phi = x*pi/4 + y*(pi/4)/dataSize; dataPhiR[ix] = (float) Math.cos(phi*mpos); dataPhiI[ix] = (float) Math.sin(phi*mpos); } } brightnessCache = 0; } double getBrightness() { if (brightnessCache != 0) return brightnessCache; int x; double avgsq = 0; double vol = 0; // we need to divide the spherical harmonic norm out of // dataR[] to get just the radial function. (The spherical // norm gets multiplied into dataR[] for efficiency.) int mpos = (m < 0) ? -m : m; double norm1 = 1/sphericalNorm(l, mpos); for (x = 0; x != dataSize; x++) { double val = dataR[x]*norm1; val *= val; avgsq += val*val*x*x; vol += x*x; } brightnessCache = avgsq / vol; return brightnessCache; } double radialNorm(int n, int l) { double a0 = factorial(n+l); return Math.sqrt(4.*factorial(n+l)/ (n*n*n*n*factorial(n-l-1)))/ factorial(2*l+1); } double sphericalNorm(int l, int m) { return Math.sqrt((2*l+1)*factorial(l-m)/ (4*pi*factorial(l+m))); } double factorial(int f) { double res = 1; while (f > 1) res *= f--; return res; } abstract void computePoint(int r, int costh); }; class SOrbital extends Orbital { SOrbital(BasisState bs) { super(bs); } void computePoint(int r, int costh) { try { float v = dataR[r]; funcr = reMult*v; funci = imMult*v; } catch (Exception e) { funcr = funci = 0; System.out.println("bad " + r + " " + costh); } } }; class MZeroOrbital extends Orbital { MZeroOrbital(BasisState bs) { super(bs); } void computePoint(int r, int costh) { try { float v = dataR[r]*dataTh[costh]; funcr = v*reMult; funci = v*imMult; } catch (Exception e) { funcr = funci = 0; System.out.println("bad " + r + " " + costh); } } }; class PairedOrbital extends Orbital { BasisState negstate; PairedOrbital(BasisState bs) { super(bs); negstate = getState(bs.n, bs.l, -bs.m); } float f1, f2, f3, f4; void setupFrame(double mult) { double a = state.re*mult; double b = state.im*mult; double c = negstate.re*mult; double d = negstate.im*mult; double mphase = Math.pow(-1, m); a *= mphase; b *= mphase; f1 = (float) (a+c); f2 = (float) (d-b); f3 = (float) (b+d); f4 = (float) (a-c); } void computePoint(int r, int costh) { try { float q = dataR[r]*dataTh[costh]; float phiValR = dataPhiR[phiIndex]; float phiValI = dataPhiI[phiIndex]; funcr = q*(f1*phiValR + f2*phiValI); funci = q*(f3*phiValR + f4*phiValI); } catch (Exception e) { funcr = funci = 0; System.out.println("bad " + r + " " + costh); } } }; class Phasor extends Rectangle { Phasor(int x, int y, int a, int b) { super(x, y, a, b); } State state; } abstract class State extends Complex { double elevel; int index; void convertDerivedToBasis() {} void convertBasisToDerived() {} void setBasisActive() {} abstract String getText(); } class BasisState extends State { int n, l, m; Orbital orb; String getText() { return "n = " + n + ", l = " + l + ", m = " + m; } } class Complex { public double re, im, mag, phase; Complex() { re = im = mag = phase = 0; } Complex(double r, double i) { set(r, i); } double magSquared() { return mag*mag; } void set(double aa, double bb) { re = aa; im = bb; setMagPhase(); } void set(double aa) { re = aa; im = 0; setMagPhase(); } void set(Complex c) { re = c.re; im = c.im; mag = c.mag; phase = c.phase; } void add(double r) { re += r; setMagPhase(); } void add(double r, double i) { re += r; im += i; setMagPhase(); } void add(Complex c) { re += c.re; im += c.im; setMagPhase(); } void addQuick(Complex c) { re += c.re; im += c.im; } void square() { set(re*re-im*im, 2*re*im); } void mult(double c, double d) { set(re*c-im*d, re*d+im*c); } void mult(double c) { re *= c; im *= c; mag *= c; } void multI() { double q = re; re = -im; im = q; } void mult(Complex c) { mult(c.re, c.im); } void setMagPhase() { mag = Math.sqrt(re*re+im*im); phase = Math.atan2(im, re); } void setMagPhase(double m, double ph) { mag = m; phase = ph; re = m*Math.cos(ph); im = m*Math.sin(ph); } void rotate(double a) { setMagPhase(mag, (phase+a) % (2*pi)); } void conjugate() { im = -im; phase = -phase; } }; class PhaseColor { public double r, g, b; PhaseColor(double rr, double gg, double bb) { r = rr; g = gg; b = bb; } } double plgndr(int l,int m,double x) { double fact,pll = 0,pmm,pmmp1,somx2; int i,ll; if (m < 0 || m > l || Math.abs(x) > 1.0) { System.out.print("bad arguments in plgndr\n"); } pmm=1.0; if (m > 0) { somx2=Math.sqrt((1.0-x)*(1.0+x)); fact=1.0; for (i=1;i<=m;i++) { pmm *= -fact*somx2; fact += 2.0; } } if (l == m) return pmm; else { pmmp1=x*(2*m+1)*pmm; if (l == (m+1)) return pmmp1; else { for (ll=(m+2);ll<=l;ll++) { pll=(x*(2*ll-1)*pmmp1-(ll+m-1)*pmm)/(ll-m); pmm=pmmp1; pmmp1=pll; } return pll; } } } double hypser(int a, int c, double z) { int n; double fac = 1; double result = 1; for (n=1;n<=1000;n++) { fac *= a*z/((double) n*c); //System.out.print("fac " + n + " " + fac + " " + z + "\n"); if (fac == 0) return result; result += fac; a++; c++; } System.out.print("convergence failure in hypser\n"); return 0; } class View extends Rectangle { View() { } View(View v) { super(v); } double scale; int paneY; int pixels[]; } void setViewMatrix(double a, double b) { int i; for (i = 0; i != 9; i++) rotmatrix[i] = 0; rotmatrix[0] = rotmatrix[4] = rotmatrix[8] = 1; rotate(a, b); } void setXYViewExact() { setViewMatrix(0, 0); } void setXZView() { setViewMatrix(0, pi/11-pi/2); } void setXZViewExact() { setViewMatrix(0, -pi/2); } void doSetup() { int s = setupChooser.getSelectedIndex(); int si = s*7; doClear(); switch (setups[si]) { case STATE_1S: getState(1, 0, 0).set(1); break; case STATE_2S: getState(2, 0, 0).set(1); break; case STATE_2P1: getState(2, 1, 1).set(1); break; case STATE_2PX: getState(2, 1, 1).set(root2inv); getState(2, 1, -1).set(-root2inv); break; case STATE_2PZ: getState(2, 1, 0).set(1); break; case STATE_3S: getState(3, 0, 0).set(1); break; case STATE_2PM1: getState(2, 1, -1).set(1); break; } speedBar.setValue(setups[si+1]); strengthBar.setValue(setups[si+2]); brightnessBar.setValue(setups[si+3]); dirChooser.select(setups[si+4]); freqChooser.select(setups[si+6]); switch (setups[si+5]) { case VIEW_XZ: setXZViewExact(); break; case VIEW_XY: setXYViewExact(); break; case VIEW_XZ12: setXZView(); break; } } int setups[] = { STATE_1S, 15, 149, 1191, DIR_X, VIEW_XZ, FREQ_12, STATE_1S, 15, 149, 1191, DIR_Z, VIEW_XZ, FREQ_12, STATE_1S, 15, 149, 1191, DIR_CCW, VIEW_XY, FREQ_12, STATE_1S, 22, 161, 1334, DIR_Z, VIEW_XZ, FREQ_13, STATE_2S, 27, 127, 1381, DIR_Z, VIEW_XZ, FREQ_23, STATE_2S, 27, 127, 1381, DIR_CCW, VIEW_XY, FREQ_23, STATE_2P1, 27, 127, 1429, DIR_CW, VIEW_XZ12, FREQ_23, STATE_2PZ, 27, 127, 1429, DIR_CCW, VIEW_XZ12, FREQ_23, STATE_2PZ, 27, 127, 1429, DIR_X, VIEW_XZ, FREQ_23, STATE_2PX, 27, 127, 1429, DIR_Z, VIEW_XZ, FREQ_23, STATE_2P1, 27, 127, 1429, DIR_Z, VIEW_XZ12, FREQ_23, STATE_3S, 27, 127, 1429, DIR_Z, VIEW_XZ, FREQ_23, STATE_2PZ, 27, 127, 1429, DIR_Z, VIEW_XZ, FREQ_23, STATE_2S, 27, 127, 1572, DIR_Z, VIEW_XZ, FREQ_24, STATE_2P1, 38, 109, 1334, DIR_CCW, VIEW_XY, FREQ_23 }; String setupStrings[] = { "1s -> 2px", "1s -> 2pz", "1s -> 2p+1", "1s -> 3pz", "2s -> 3pz", "2s -> 3p+1", "2p+1 -> 3s+3dz2", "2pz -> 3d+1", "2pz -> 3dxz", "2px -> 3dxz", "2p+1 -> 3d+1", "3s -> 3dz2+3s", "2pz -> 3dz2", "2s -> 4pz", "2p+1 -> 3d+2", null }; }