// QuantumOsc3d.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.util.Arrays; import java.awt.image.MemoryImageSource; import java.lang.Math; import java.awt.event.*; class QuantumOsc3dCanvas extends Canvas { QuantumOsc3dFrame pg; QuantumOsc3dCanvas(QuantumOsc3dFrame p) { pg = p; } public Dimension getPreferredSize() { return new Dimension(300,400); } public void update(Graphics g) { pg.updateQuantumOsc3d(g); } public void paint(Graphics g) { pg.updateQuantumOsc3d(g); } }; class QuantumOsc3dLayout implements LayoutManager { public QuantumOsc3dLayout() {} 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 Label) { h += d.height/5; d.width = barwidth; } m.move(cw, h); m.resize(d.width, d.height); h += d.height; } } } }; public class QuantumOsc3d extends Applet { static QuantumOsc3dFrame oc; void destroyFrame() { if (oc != null) oc.dispose(); oc = null; } public static void main(String args[]) { oc = new QuantumOsc3dFrame(null); oc.init(); } public void init() { oc = new QuantumOsc3dFrame(this); oc.init(); } public void destroy() { if (oc != null) oc.dispose(); oc = null; } }; class QuantumOsc3dFrame extends Frame implements ComponentListener, ActionListener, AdjustmentListener, MouseMotionListener, MouseListener, ItemListener { Thread engine = null; Dimension winSize; Image dbimage; Random random; int gridSizeX = 200; int gridSizeY = 200; public String getAppletInfo() { return "QuantumOsc3d by Paul Falstad"; } Button blankButton; Button normalizeButton; Button maximizeButton; Checkbox memoryImageSourceCheck; Checkbox stoppedCheck; CheckboxMenuItem colorCheck; CheckboxMenuItem eCheckItem; CheckboxMenuItem xCheckItem; CheckboxMenuItem lCheckItem; CheckboxMenuItem alwaysNormItem; CheckboxMenuItem axesItem; CheckboxMenuItem autoZoomItem; CheckboxMenuItem animatedZoomItem; Menu measureMenu, presetsMenu; MenuItem exitItem; MenuItem measureEItem; MenuItem measureLxItem; MenuItem measureLyItem; MenuItem measureLzItem; MenuItem dispGaussItem; MenuItem scaled1GaussItem; MenuItem scaled2GaussItem; MenuItem rotatingGaussItem; MenuItem dispX110Item; MenuItem dispZ110Item; Choice modeChooser; Choice viewChooser; Choice sliceChooser, nChooser, lChooser, mChooser; static final int SLICE_NONE = 0; static final int SLICE_X = 1; static final int SLICE_Y = 2; static final int SLICE_Z = 3; Scrollbar speedBar; Scrollbar resolutionBar; Scrollbar internalResBar; Scrollbar brightnessBar; Scrollbar scaleBar; Scrollbar sampleBar; View viewPotential, viewX, viewL, viewStates; View viewList[]; int viewCount; Orbital orbitals[]; int orbCount; Phasor phasors[]; int phasorCount; BasisState states[]; int stateCount; AlternateBasis rectBasis, lxBasis, lyBasis; AlternateBasis basisList[]; int basisCount; TextBox textBoxes[]; int textCount; boolean changingDerivedStates; double dragZoomStart; double zoom; // was 10 double rotmatrix[]; Rectangle viewAxes; static final double pi = 3.14159265358979323846; static final double pi2 = pi*2; static final double root2 = 1.41421356237309504880; static final double root2inv = .70710678118654752440; static final double baseEnergy = 0; int xpoints[]; int ypoints[]; int selectedPaneHandle; double func[][][]; PhaseColor phaseColors[][]; 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; QuantumOsc3d 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_ROTATE_X = 1; static final int MODE_ROTATE_Y = 2; static final int MODE_ROTATE_Z = 3; static final int MODE_SLICE = 5; static final int VIEW_COMPLEX = 0; static final int VIEW_COMBO_COMP = 1; static final int VIEW_COMBO_RECT = 2; static final int VIEW_COMBO_N1 = 3; static final int VIEW_COMBO_N2 = 4; static final int VIEW_COMBO_N3 = 5; static final int VIEW_COMBO_N4 = 6; 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; double funcr, funci; int phiIndex, phiSector; 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; } QuantumOsc3dCanvas cv; QuantumOsc3dFrame(QuantumOsc3d a) { super("3-D Quantum Oscillator Viewer"); applet = a; } 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 = 100; // change settings to speed things up where possible if (os.indexOf("Windows") == 0) { res = 100; if (jv.indexOf("1.1") == 0) altRender = true; } setLayout(new QuantumOsc3dLayout()); cv = new QuantumOsc3dCanvas(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.add(lCheckItem = getCheckItem("Angular Momentum")); m.addSeparator(); m.add(colorCheck = getCheckItem("Phase as Color")); colorCheck.setState(true); measureMenu = m = new Menu("Measure"); mb.add(m); m.add(measureEItem = getMenuItem("Measure Energy")); m.add(measureLxItem = getMenuItem("Measure Lx")); m.add(measureLyItem = getMenuItem("Measure Ly")); m.add(measureLzItem = getMenuItem("Measure Lz")); setMenuBar(mb); m = new Menu("Options"); mb.add(m); alwaysNormItem = getCheckItem("Always Normalize"); m.add(axesItem = getCheckItem("Show Axes")); axesItem.setState(true); m.add(autoZoomItem = getCheckItem("Auto Scale")); autoZoomItem.setState(true); m.add(animatedZoomItem = getCheckItem("Animated Scaling")); animatedZoomItem.setState(true); presetsMenu = m = new Menu("Presets"); mb.add(m); m.add(dispGaussItem = getMenuItem("Displaced Gaussian")); m.add(scaled1GaussItem = getMenuItem("Scaled Gaussian 1")); m.add(scaled2GaussItem = getMenuItem("Scaled Gaussian 2")); m.add(rotatingGaussItem = getMenuItem("Rotating Gaussian")); m.add(dispX110Item = getMenuItem("1,0,1 Displaced X")); m.add(dispZ110Item = getMenuItem("1,0,0 Displaced Z")); setMenuBar(mb); viewChooser = new Choice(); viewChooser.add("Single Wave Functions"); viewChooser.add("Combinations"); viewChooser.add("Rectangular Combos"); viewChooser.add("Multiple Bases (n=1)"); viewChooser.add("Multiple Bases (n=2)"); viewChooser.add("Multiple Bases (n=3)"); viewChooser.add("Multiple Bases (n=4)"); viewChooser.addItemListener(this); add(viewChooser); int i; nChooser = new Choice(); for (i = 0; i <= maxnr; i++) nChooser.add("nr = " + i); nChooser.addItemListener(this); add(nChooser); nChooser.select(0); lChooser = new Choice(); for (i = 0; i <= maxl; i++) lChooser.add("l = " + i + ((i < 6) ? " (" + codeLetter[i] + ")" : "")); lChooser.addItemListener(this); add(lChooser); mChooser = new Choice(); mChooser.addItemListener(this); add(mChooser); 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); modeChooser = new Choice(); modeChooser.add("Mouse = Adjust View"); modeChooser.add("Mouse = Rotate X"); modeChooser.add("Mouse = Rotate Y"); modeChooser.add("Mouse = Rotate Z"); modeChooser.addItemListener(this); add(modeChooser); stoppedCheck = new Checkbox("Stopped"); stoppedCheck.addItemListener(this); add(stoppedCheck); add(blankButton = new Button("Clear")); blankButton.addActionListener(this); add(normalizeButton = new Button("Normalize")); normalizeButton.addActionListener(this); add(maximizeButton = new Button("Maximize")); maximizeButton.addActionListener(this); lChooser.select(3); setLValue(); memoryImageSourceCheck = new Checkbox("Alternate Rendering", altRender); memoryImageSourceCheck.addItemListener(this); add(memoryImageSourceCheck); add(new Label("Simulation Speed", Label.CENTER)); add(speedBar = new Scrollbar(Scrollbar.HORIZONTAL, 20, 1, 1, 200)); speedBar.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, 500)); resolutionBar.addAdjustmentListener(this); /*add(new Label("Internal Resolution", Label.CENTER)); add(internalResBar = new Scrollbar(Scrollbar.HORIZONTAL, res, 2, 20, 200)); internalResBar.addAdjustmentListener(this);*/ add(new Label("Scale", Label.CENTER)); add(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+1]; for (i = 0; i != 8; i++) for (j = 0; j <= phaseColorCount; j++) { double ang = java.lang.Math.atan(j/(double) phaseColorCount); phaseColors[i][j] = genPhaseColor(i, ang); } 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(); random = new Random(); reinit(); orbitalChanged(); cv.setBackground(Color.black); cv.setForeground(Color.white); resize(680, 650); handleResize(); show(); } static final int maxnr = 11; static final int maxl = 10; void setupStates() { stateCount = (maxnr+1) * ((maxl+1)*(maxl+1)); int i; states = new BasisState[stateCount]; int nr = 0; int l = 0; int m = 0; for (i = 0; i != stateCount; i++) { BasisState bs = states[i] = new BasisState(); bs.elevel = 2*nr+l+1.5; bs.nr = nr; bs.l = l; bs.m = m; bs.n = 2*nr+l; if (m < l) m++; else { l++; if (l <= maxl) m = -l; else { nr++; l = m = 0; } } } basisList = new AlternateBasis[17]; basisCount = 0; rectBasis = setupRectBasis(); lxBasis = initBasis(35, true); setupLBasis(lxBasis, 0, 0, true, l0Array); setupLBasis(lxBasis, 0, 1, true, l1xArray); setupLBasis(lxBasis, 0, 2, true, l2xArray); setupLBasis(lxBasis, 0, 3, true, l3xArray); setupLBasis(lxBasis, 0, 4, true, l4xArray); setupLBasis(lxBasis, 1, 0, true, l0Array); setupLBasis(lxBasis, 1, 1, true, l1xArray); setupLBasis(lxBasis, 1, 2, true, l2xArray); setupLBasis(lxBasis, 2, 0, true, l0Array); lyBasis = initBasis(35, true); setupLBasis(lyBasis, 0, 0, false, l0Array); setupLBasis(lyBasis, 0, 1, false, l1yArray); setupLBasis(lyBasis, 0, 2, false, l2yArray); setupLBasis(lyBasis, 0, 3, false, l3yArray); setupLBasis(lyBasis, 0, 4, false, l4yArray); setupLBasis(lyBasis, 1, 0, false, l0Array); setupLBasis(lyBasis, 1, 1, false, l1yArray); setupLBasis(lyBasis, 1, 2, false, l2yArray); setupLBasis(lyBasis, 2, 0, false, l0Array); } AlternateBasis initBasis(int sct, boolean xAxis) { AlternateBasis basis = new AlternateBasis(); basis.xAxis = xAxis; basis.altStates = new DerivedState[sct]; basis.altStateCount = 0; return basis; } void setupLBasis(AlternateBasis basis, int nr, int l, boolean xAxis, double arr[]) { String mtext = (xAxis) ? "mx" : "my"; int i; int lct = l*2+1; int ap = 0; for (i = 0; i != lct; i++) { int sn = basis.altStateCount++; DerivedState ds = basis.altStates[sn] = new DerivedState(); ds.basis = basis; ds.count = lct; ds.bstates = new BasisState[lct]; ds.coefs = new Complex[lct]; ds.m = i-l; ds.l = l; ds.nr = nr; ds.n = 2*nr+l; ds.elevel = 2*nr+l+1.5; int j; for (j = 0; j != lct; j++) { ds.bstates[j] = getState(nr, l, j-l); ds.coefs[j] = new Complex(); } ds.text = "n = " + ds.n + ", nr = " + nr + ", l = " + l + ", " + mtext + " = " + ds.m; for (j = 0; j != lct; j++) { ds.coefs[j].set(arr[ap], arr[ap+1]); ap += 2; } } } AlternateBasis setupRectBasis() { int sct = 35; AlternateBasis basis = new AlternateBasis(); basis.altStates = new DerivedState[sct]; basis.altStateCount = sct; int i; int nx = 0, ny = 0, nz = 0; int ap = 0; for (i = 0; i != sct; i++) { int n = nx+ny+nz; int n21 = n/2+1; int l = ((n & 1) == 0) ? 0 : 1; int nr = n/2; int m = -l; DerivedState ds = basis.altStates[i] = new DerivedState(); ds.basis = basis; ds.count = (l == 0) ? 2*n21*n21-n21 : 2*n21*n21+n21; ds.bstates = new BasisState[sct]; ds.coefs = new Complex[sct]; ds.text = "nx = " + nx + ", ny = " + ny + ", nz = " + nz; ds.nx = nx; ds.ny = ny; ds.nz = nz; ds.n = n; ds.elevel = 2*nr+l+1.5; int j; for (j = 0; j != ds.count; j++) { ds.bstates[j] = getState(nr, l, m); ds.coefs[j] = new Complex(rectArrayR[ap], rectArrayI[ap]); ap++; if (m++ == l) { l += 2; nr--; m = -l; } } if (i == sct-1) break; do { nz++; if (nz > 4) { nz = 0; nx++; if (nx > 4) { nx = 0; ny++; } } } while (nx+ny+nz > 4); } return basis; } // Lx and Ly eigenvectors for various values of l, expressed in // terms of Lz eigenvectors double l0Array[] = {1, 0}; double l1xArray[] = { .5, 0, -root2inv, 0, .5, 0, root2inv, 0, 0, 0, -root2inv, 0, .5, 0, root2inv, 0, .5, 0 }; double l1yArray[] = { .5, 0, 0, -root2inv, -.5, 0, 0, -root2inv, 0, 0, 0, -root2inv, .5, 0, 0, root2inv, -.5, 0 }; static final double root6by4 = .61237243569579452454; double l2xArray[] = { 1/4., 0, -1/2., 0, root6by4, 0, -1/2., 0, 1/4., 0, -.5, 0, .5, 0, 0, 0, -.5, 0, .5, 0, root6by4, 0, 0, 0, -.5, 0, 0, 0, root6by4, 0, -.5, 0, -.5, 0, 0, 0, .5, 0, .5, 0, 1/4., 0, 1/2., 0, root6by4, 0, 1/2., 0, 1/4., 0 }; double l2yArray[] = { 1/4., 0, 0, -1/2., -root6by4, 0, 0, 1/2., 1/4., 0, -.5, 0, 0, .5, 0, 0, 0, .5, .5, 0, -root6by4, 0, 0, 0, -.5, 0, 0, 0, -root6by4, 0, -.5, 0, 0, -.5, 0, 0, 0, -.5, .5, 0, 1/4., 0, 0, 1/2., -root6by4, 0, 0, -1/2., 1/4., 0 }; double l3xArray[] = { 0.125,0, -0.306186,0, 0.484123,0, -0.559017,0, 0.484123,0, -0.306186,0, 0.125,0, -0.306186,0, 0.5,0, -0.395285,0, 0.,0, 0.395285,0, -0.5,0, 0.306186,0, 0.484123,0, -0.395285,0, -0.125,0, 0.433013,0, -0.125,0, -0.395285,0, 0.4841230,0, 0.559017,0, 0.,0, -0.433013,0, 0.,0, 0.433013,0, 0.,0, -0.559017,0, 0.484123,0, 0.395285,0, -0.125,0, -0.433013,0, -0.125,0, 0.395285,0, 0.484123,0, -0.306186,0, -0.5,0, -0.395285,0, 0.,0, 0.395285,0, 0.5,0, 0.306186,0, 0.125,0, 0.306186,0, 0.484123,0, 0.559017,0, 0.484123,0, 0.306186,0, 0.125,0 }; double l3yArray[] = { -0.125,0, 0,0.306186, 0.484123,0, 0,-0.559017, -0.484123,0, 0,0.306186, 0.125,0, 0.306186,0, 0,-0.5, -0.395285,0, 0.,0, -0.395285,0, 0,0.5, 0.306186,0, -0.484123,0, 0,0.395285, -0.125,0, 0,0.433013, 0.125,0, 0,0.395285, 0.484123,0, 0,0.559017, 0.,0, 0,0.433013, 0.,0, 0,0.433013, 0.,0, 0,0.559017, -0.484123,0, 0,-0.395285, -0.125,0, 0,-0.433013, 0.125,0, 0,-0.395285, 0.484123,0, 0.306186,0, 0,+0.5, -0.395285,0, 0.,0, -0.395285,0, 0,-0.5, 0.306186,0, -0.125,0, 0,-0.306186, 0.484123,0, 0,+0.559017, -0.484123,0, 0,-0.306186, 0.125,0 }; double l4xArray[] = { 0.0625, 0., -0.176777, 0., 0.330719, 0., -0.467707, 0., 0.522913, 0., -0.467707, 0., 0.330719, 0., -0.176777, 0., 0.0625, 0., -0.176777, 0., 0.375, 0., -0.467707, 0., 0.330719, 0., 0., 0., -0.330719, 0., 0.467707, 0., -0.375, 0., 0.176777, 0., 0.330719, 0., -0.467707, 0., 0.25, 0., 0.176777, 0., -0.395285, 0., 0.176777, 0., 0.25, 0., -0.467707, 0., 0.330719, 0., -0.467707, 0., 0.330719, 0., 0.176777, 0., -0.375, 0., 0., 0., 0.375, 0., -0.176777, 0., -0.330719, 0., 0.467707, 0., 0.522913, 0., 0., 0., -0.395285, 0., 0., 0., 0.375, 0., 0., 0., -0.395285, 0., 0., 0., 0.522913, 0., -0.467707, 0., -0.330719, 0., 0.176777, 0., 0.375, 0., 0., 0., -0.375, 0., -0.176777, 0., 0.330719, 0., 0.467707, 0., 0.330719, 0., 0.467707, 0., 0.25, 0., -0.176777, 0., -0.395285, 0., -0.176777, 0., 0.25, 0., 0.467707, 0., 0.330719, 0., -0.176777, 0., -0.375, 0., -0.467707, 0., -0.330719, 0., 0., 0., 0.330719, 0., 0.467707, 0., 0.375, 0., 0.176777, 0., 0.0625, 0., 0.176777, 0., 0.330719, 0., 0.467707, 0., 0.522913, 0., 0.467707, 0., 0.330719, 0., 0.176777, 0., 0.0625, 0. }; double l4yArray[] = { 0.0625, 0., 0., -0.176777, -0.330719, 0., 0., 0.467707, 0.522913, 0., 0., -0.467707, -0.330719, 0., 0., 0.176777, 0.0625, 0., -0.176777, 0., 0., 0.375, 0.467707, 0., 0., -0.330719, 0., 0., 0., -0.330719, -0.467707, 0., 0., 0.375, 0.176777, 0., 0.330719, 0., 0., -0.467707, -0.25, 0., 0., -0.176777, -0.395285, 0., 0., 0.176777, -0.25, 0., 0., 0.467707, 0.330719, 0., -0.467707, 0., 0., 0.330719, -0.176777, 0., 0., 0.375, 0., 0., 0., 0.375, 0.176777, 0., 0., 0.330719, 0.467707, 0., 0.522913, 0., 0., 0., 0.395285, 0., 0., 0., 0.375, 0., 0., 0., 0.395285, 0., 0., 0., 0.522913, 0., -0.467707, 0., 0., -0.330719, -0.176777, 0., 0., -0.375, 0., 0., 0., -0.375, 0.176777, 0., 0., -0.330719, 0.467707, 0., 0.330719, 0., 0., 0.467707, -0.25, 0., 0., 0.176777, -0.395285, 0., 0., -0.176777, -0.25, 0., 0., -0.467707, 0.330719, 0., -0.176777, 0., 0., -0.375, 0.467707, 0., 0., 0.330719, 0., 0., 0., 0.330719, -0.467707, 0., 0., -0.375, 0.176777, 0., 0.0625, 0., 0., 0.176777, -0.330719, 0., 0., -0.467707, 0.522913, 0., 0., 0.467707, -0.330719, 0., 0., -0.176777, 0.0625, 0. }; // precomputed using brute-force mathematica code double rectArrayR[] = { 1.,0.,1.,0.,-0.57735,0.,0.,0.816497,0.,0.,0.,-0.774597,0.,0.,0., 0.,0.632456,0.,0.,0.,0.447214,0.,0.,-0.755929,0.,0.,0.,0.,0.,0., 0.478091,0.,0.,0.,0.,0.707107,0.,-0.707107,0.,0.,0.707107,0., -0.707107,0.,-0.316228,0.,0.316228,0.,0.,0.632456,0.,-0.632456, 0.,0.,0.,0.,-0.46291,0.,0.46291,0.,0.,0.,0.,0.534522,0., -0.534522,0.,0.,0.,-0.57735,0.5,0.,-0.408248,0.,0.5,0., -0.447214,0.,0.,0.5,0.,-0.547723,0.,0.5,0.,0.365148,-0.188982,0., -0.154303,0.,-0.188982,0.,0.,0.46291,0.,-0.58554,0.,0.46291,0.,0., -0.547723,0.,0.547723,0.353553,0.,-0.273861,0.,0.273861,0., -0.353553,0.,0.,-0.46291,0.,0.46291,0.,0.,0.353553,0.,-0.400892, 0.,0.400892,0.,-0.353553,0.,0.447214,-0.46291,0.,0.377964,0., -0.46291,0.25,0.,-0.188982,0.,0.179284,0.,-0.188982,0.,0.25,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., 0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0., -0.57735,-0.5,0.,-0.408248,0.,-0.5,0.,-0.447214,0.,0.,-0.5,0., -0.547723,0.,-0.5,0.,0.365148,0.188982,0.,-0.154303,0.,0.188982, 0.,0.,-0.46291,0.,-0.58554,0.,-0.46291,0.,0.,-0.316228,0.,0.316228, -0.612372,0.,-0.158114,0.,0.158114,0.,0.612372,0.,0., -0.267261,0.,0.267261,0.,0.,-0.612372,0.,-0.231455,0., 0.231455,0.,0.612372,0.,0.365148,0.,0.,0.308607,0.,0., -0.612372,0.,0.,0.,0.146385,0.,0.,0.,-0.612372,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.447214,0.46291,0., 0.377964,0.,0.46291,0.25,0.,0.188982,0.,0.179284,0.,0.188982,0.,0.25 }; double rectArrayI[] = { 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.,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.707107,0.,-0.707107,0.,0.,-0.707107,0., -0.707107,0.,0.316228,0.,0.316228,0.,0.,-0.632456,0.,-0.632456,0., 0.,0.,0.,0.46291,0.,0.46291,0.,0.,0.,0.,-0.534522,0.,-0.534522,0., 0.,0.,0.,-0.707107,0.,0.,0.,0.707107,0.,0.,0.,0.,-0.707107,0.,0.,0., 0.707107,0.,0.,0.267261,0.,0.,0.,-0.267261,0.,0.,-0.654654,0.,0.,0., 0.654654,0.,0.,0.316228,0.,0.316228,-0.612372,0.,0.158114,0., 0.158114,0.,-0.612372,0.,0.,0.267261,0.,0.267261,0.,0.,-0.612372, 0.,0.231455,0.,0.231455,0.,-0.612372,0.,0.,0.46291,0.,0.,0., -0.46291,-0.5,0.,0.188982,0.,0.,0.,-0.188982,0.,0.5,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.547723,0.,0.547723,0.353553,0.,0.273861,0.,0.273861,0.,0.353553, 0.,0.,0.46291,0.,0.46291,0.,0.,0.353553,0.,0.400892,0.,0.400892,0., 0.353553,0.,0.,0.46291,0.,0.,0.,-0.46291,0.5,0.,0.188982,0.,0.,0., -0.188982,0.,-0.5,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,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 = 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 setupMenus() { switch (viewChooser.getSelectedIndex()) { case VIEW_COMPLEX: nChooser.show(); lChooser.show(); mChooser.show(); modeChooser.hide(); modeChooser.select(MODE_ANGLE); blankButton.hide(); normalizeButton.hide(); maximizeButton.hide(); alwaysNormItem.disable(); measureMenu.disable(); break; default: nChooser.hide(); lChooser.hide(); mChooser.hide(); modeChooser.show(); blankButton.show(); normalizeButton.show(); maximizeButton.show(); alwaysNormItem.enable(); measureMenu.enable(); break; } switch (viewChooser.getSelectedIndex()) { case VIEW_COMBO_COMP: case VIEW_COMBO_RECT: presetsMenu.enable(); break; default: presetsMenu.disable(); break; } validate(); } void createPhasors() { phasorCount = textCount = 0; int i; for (i = 0; i != basisCount; i++) basisList[i].active = false; if (viewStates == null) return; int sz = viewStates.height/4; int x = 0; int y = viewStates.y; int y0 = y; int nr = 0, l = 0, m = 0; int sz2; textBoxes = new TextBox[10]; switch (viewChooser.getSelectedIndex()) { case VIEW_COMPLEX: break; case VIEW_COMBO_COMP: phasorCount = 25*4; phasors = new Phasor[phasorCount]; sz2 = viewStates.width / 25; if (sz > sz2) sz = sz2; if (sz < 10) sz = 10; for (i = 0; i != phasorCount; i++) { Phasor ph = phasors[i] = new Phasor(x, y, sz, sz); ph.state = getState(nr, l, m); x += sz; if (++m > l) { y += sz/2; l++; m = -l; if (l >= 5) { x = 0; y0 += sz; y = y0; nr++; l = m = 0; } } } break; case VIEW_COMBO_RECT: sz = viewStates.height/5; phasorCount = rectBasis.altStateCount; phasors = new Phasor[phasorCount]; i = 0; for (m = 0; m != 5; m++) { i = createRectPhasors(x, y, sz, i, 5-m); x += (5-m)*sz + sz/2; } break; case VIEW_COMBO_N1: phasorCount = 12; phasors = new Phasor[phasorCount]; i = 0; i = createBasisPhasors(x, y, sz, i, 0, 1); createText("Lz", x+sz*3, y, sz); y += sz; i = createAltPhasors(x, y, sz, i, lxBasis, 3, 1); createText("Lx", x+sz*3, y, sz); y += sz; i = createAltPhasors(x, y, sz, i, lyBasis, 3, 1); createText("Ly", x+sz*3, y, sz); y += sz; i = createRectPhasorsN(x, y, sz, i, rectBasis, 1); createText("Rect", x+sz*3, y, sz); break; case VIEW_COMBO_N2: phasorCount = 6*4; phasors = new Phasor[phasorCount]; i = 0; i = createBasisPhasors(x, y, sz, i, 1, 0); i = createBasisPhasors(x+sz*2, y, sz, i, 0, 2); createText("Lz", x+sz*7, y, sz); y += sz; i = createAltPhasors(x, y, sz, i, lxBasis, 1, 25); i = createAltPhasors(x+sz*2, y, sz, i, lxBasis, 5, 4); createText("Lx", x+sz*7, y, sz); y += sz; i = createAltPhasors(x, y, sz, i, lyBasis, 1, 25); i = createAltPhasors(x+sz*2, y, sz, i, lyBasis, 5, 4); createText("Ly", x+sz*7, y, sz); y += sz; i = createRectPhasorsN(x, y, sz, i, rectBasis, 2); createText("Rect", x+sz*7, y, sz); break; case VIEW_COMBO_N3: phasorCount = 10*4; phasors = new Phasor[phasorCount]; i = 0; i = createBasisPhasors(x, y, sz, i, 1, 1); i = createBasisPhasors(x+sz*4, y, sz, i, 0, 3); createText("Lz", x+sz*12, y, sz); y += sz; i = createAltPhasors(x, y, sz, i, lxBasis, 3, 26); i = createAltPhasors(x+sz*4, y, sz, i, lxBasis, 7, 9); createText("Lx", x+sz*12, y, sz); y += sz; i = createAltPhasors(x, y, sz, i, lyBasis, 3, 26); i = createAltPhasors(x+sz*4, y, sz, i, lyBasis, 7, 9); createText("Ly", x+sz*12, y, sz); y += sz; i = createRectPhasorsN(x, y, sz, i, rectBasis, 3); createText("Rect", x+sz*12, y, sz); break; case VIEW_COMBO_N4: phasorCount = 15*4; phasors = new Phasor[phasorCount]; i = 0; i = createBasisPhasors(x, y, sz, i, 2, 0); i = createBasisPhasors(x+sz*2, y, sz, i, 1, 2); i = createBasisPhasors(x+sz*8, y, sz, i, 0, 4); createText("Lz", x+sz*17, y, sz); y += sz; i = createAltPhasors(x, y, sz, i, lxBasis, 1, 34); i = createAltPhasors(x+sz*2, y, sz, i, lxBasis, 5, 29); i = createAltPhasors(x+sz*8, y, sz, i, lxBasis, 9, 16); createText("Lx", x+sz*17, y, sz); y += sz; i = createAltPhasors(x, y, sz, i, lyBasis, 1, 34); i = createAltPhasors(x+sz*2, y, sz, i, lyBasis, 5, 29); i = createAltPhasors(x+sz*8, y, sz, i, lyBasis, 9, 16); createText("Ly", x+sz*17, y, sz); y += sz; i = createRectPhasorsN(x, y, sz, i, rectBasis, 4); createText("Rect", x+sz*17, y, sz); break; } for (i = 0; i != phasorCount; i++) phasors[i].state.setBasisActive(); for (i = 0; i != basisCount; i++) { if (basisList[i].active) { // this clears out any states which do not have phasors present basisList[i].convertBasisToDerived(); basisList[i].convertDerivedToBasis(); } } // and if we're viewing Complex Combos, we need an extra step // to clear out any states with phasors not present. All other // views are handled by the previous loop. if (viewChooser.getSelectedIndex() == VIEW_COMBO_COMP) for (i = 0; i != stateCount; i++) if (states[i].nr >= 4 || states[i].l >= 5) states[i].set(0); // in case the states changed createOrbitals(); } boolean higherStatesPresent() { int i; for (i = 0; i != stateCount; i++) if (states[i].n > 4 && states[i].mag > 0) return true; return false; } void setInitialOrbital() { if (phasorCount == 0) return; int i; for (i = 0; i != phasorCount; i++) if (phasors[i].state.mag > 0) return; doClear(); // no states active, so pick a phasor and select it. phasors[0].state.set(1); createOrbitals(); } int createBasisPhasors(int x, int y, int sz, int i, int nr, int l) { int j; for (j = 0; j != l*2+1; j++) { Phasor ph = phasors[i] = new Phasor(x, y, sz, sz); ph.state = getState(nr, l, j-l); x += sz; i++; } return i; } int createAltPhasors(int x, int y, int sz, int i, AlternateBasis basis, int ct, int offset) { int j; for (j = 0; j != ct; j++) { Phasor ph = phasors[i] = new Phasor(x, y, sz, sz); ph.state = basis.altStates[j+offset]; x += sz; i++; } return i; } int createRectPhasorsN(int x, int y, int sz, int i, AlternateBasis basis, int n) { int j; for (j = 0; j != basis.altStateCount; j++) { DerivedState ds = basis.altStates[j]; if (ds.nx+ds.ny+ds.nz != n) continue; Phasor ph = phasors[i] = new Phasor(x, y, sz, sz); ph.state = basis.altStates[j]; x += sz; i++; } return i; } int createRectPhasors(int x, int y, int sz, int i, int nz) { while (nz > 0) { i = createAltPhasors(x, y, sz, i, rectBasis, nz, i); y += sz; nz--; } return i; } void createText(String text, int x, int y, int sz) { TextBox tb = new TextBox(x+10, y, winSize.width-x, sz, text); textBoxes[textCount++] = tb; } void setupDisplay() { if (winSize == null) return; int potsize = (viewPotential == null) ? 50 : viewPotential.height; int statesize = (viewStates == null) ? 64 : viewStates.height; viewX = viewPotential = viewL = 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(); if (lCheckItem.getState()) viewList[i++] = viewL = new View(); if (viewChooser.getSelectedIndex() > VIEW_COMPLEX) 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 = 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); int asize = (int) (min(viewX.width, viewX.height)/3); viewAxes = new Rectangle(viewX.x+winSize.width-asize, viewX.y, asize, asize); setupMenus(); createPhasors(); } int getTermWidth() { return 8; } // multiply rotation matrix by rotations through angle1 and angle2 void rotate(double angle1, double angle2) { double r1cos = java.lang.Math.cos(angle1); double r1sin = java.lang.Math.sin(angle1); double r2cos = java.lang.Math.cos(angle2); double r2sin = java.lang.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(); func = new double[gridSizeX][gridSizeY][3]; } int getNR() { return nChooser.getSelectedIndex(); } int getL() { return lChooser.getSelectedIndex(); } int getM() { return mChooser.getSelectedIndex() - getL(); } String codeLetter[] = { "s", "p", "d", "f", "g", "h" }; void setLValue() { int l = getL(); int i; mChooser.removeAll(); for (i = -l; i <= l; i++) mChooser.add("m = " + i); mChooser.select(l); validate(); } // compute func[][][] array (2-d view) by raytracing through a // 3-d dataset (data[][][]) void computeView(double colorMult, 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 = java.lang.Math.sqrt(camvx0*camvx0+camvy0*camvy0+1); int n; double simpr = 0; double simpg = 0; double 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; func[i][j][0] = func[i][j][1] = func[i][j][2] = 0; if (discrim < 0) { // doesn't hit it continue; } discrim = java.lang.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) continue; mint = maxt = t; } // sample evenly along intersecting portion double tstep = (maxt-mint)/(sampleCount-1); double pathlen = (maxt-mint)*camnorm; int maxn = sampleCount; double xx = (camx + camvx * mint) * xmult; double yy = (camy + camvy * mint) * ymult; double zz = (camz + camvz * mint) * zmult; if (slice != SLICE_NONE) { maxn = 1; pathlen = 2; if (xx > xmult || yy > ymult || zz > zmult || xx < -xmult || yy < -ymult || zz < -zmult) continue; } camvx *= tstep*xmult; camvy *= tstep*ymult; camvz *= tstep*zmult; int dshalf = dataSize/2; int oi; for (n = 0; n < maxn; n++) { // find grid element that contains sampled point double r = java.lang.Math.sqrt(xx*xx+yy*yy+zz*zz); double costh = zz/r; int ri = (int) r; int costhi = (int) (costh*dshalf+dshalf); double 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 { double 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; func[i][j][0] = simpr; func[i][j][1] = simpg; func[i][j][2] = simpb; } } PhaseColor getPhaseColor(double x, double y) { int sector = 0; double val = 0; if (x == 0 && y == 0) return phaseColors[0][0]; if (y >= 0) { if (x >= 0) { if (x >= y) { sector = 0; val = y/x; } else { sector = 1; val = 1-x/y; } } else { if (-x <= y) { sector = 2; val = -x/y; } else { sector = 3; val = 1+y/x; } } } else { if (x <= 0) { if (y >= x) { sector = 4; val = y/x; } else { sector = 5; val = 1-x/y; } } else { if (-y >= x) { sector = 6; val = -x/y; } else { sector = 7; val = 1+y/x; } } } return phaseColors[sector][(int) (val*phaseColorCount)]; } void calcPhiComponent(double x, double y) { phiSector = 0; double val = 0; if (x == 0 && y == 0) { phiSector = 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 = (int) (val*dataSize); } void setScale() { if (manualScale || !autoZoomItem.getState()) return; int i; double outer = 0; for (i = 0; i != orbCount; i++) { Orbital orb = orbitals[i]; double r = orb.getScaleRadius(); if (r > outer) outer = r; } // 148 is fudge factor determined by trial and error int scaleValue = (int) (outer*148); 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(); } public void updateQuantumOsc3d(Graphics realg) { Graphics g = null; if (winSize == null || winSize.width == 0) return; boolean mis = memoryImageSourceCheck.getState(); 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; double tadd = 0; if (!stoppedCheck.getState()) { int val = speedBar.getValue(); tadd = val*(.1/16); t += tadd; } else allQuiet = true; double norm = 0; double normmult = 0, normmult2 = 0; if (alwaysNormItem.getState()) normalize(); // update phases int i; for (i = 0; i != stateCount; i++) { State st = states[i]; if (st.mag < epsilon) { st.set(0); continue; } if (tadd != 0) { allQuiet = false; st.rotate(-(st.elevel+baseEnergy)*tadd); } norm += st.magSquared(); } normmult2 = 1/norm; if (norm == 0) normmult2 = 0; normmult = java.lang.Math.sqrt(normmult2); AlternateBasis skipBasis = (changingDerivedStates) ? ((DerivedState) selectedState).basis : null; for (i = 0; i != basisCount; i++) { AlternateBasis basis = basisList[i]; if (basis != skipBasis && basis.active) basis.convertBasisToDerived(); } setScale(); setBrightness(normmult2); boolean sliced = sliceChooser.getSelectedIndex() != SLICE_NONE; zoom = (sliced) ? 8 : 16.55; double colorMult = java.lang.Math.exp(brightnessBar.getValue()/100.); //System.out.println(colorMult); computeView(colorMult, 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 = 10; // viewPotential.height * 1.9; g.setColor(Color.darkGray); int floory = viewPotential.y + viewPotential.height - 1; for (i = 0; i != 50; i++) { double e = (i + 1.5); int y = floory - (int) (ymult * e); if (y >= 0) g.drawLine(0, y, winSize.width, y); } double xp = getScaler(); g.setColor(Color.white); int ox = -1, oy = -1; int x; for (x = 0; x != winSize.width; x++) { double xx = (x-winSize.width/2)*xp; double dy = .5 * xx*xx; int y = floory - (int) (ymult * dy); if (y < 0) { if (ox == -1) continue; g.drawLine(ox, oy, ox, 0); ox = -1; continue; } if (ox == -1 && x > 0) { g.drawLine(x, 0, 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 = floory - (int) (ymult * expecte); g.setColor(Color.red); g.drawLine(0, y, winSize.width, y); } if (selectedState != null && !dragging) { g.setColor(Color.yellow); int y = floory - (int) (ymult * selectedState.elevel); g.drawLine(0, y, winSize.width, y); } } if (viewL != null) { int maxm = 4; int pad = 3; int ct = (maxm*2+1)*pad; double ldata[] = new double[ct]; if (!higherStatesPresent()) { calcLxy(lxBasis, ldata, ct, maxm, pad, true, false); drawFunction(g, viewL, 0, ldata, ct, pad, false); calcLxy(lyBasis, ldata, ct, maxm, pad, false, false); drawFunction(g, viewL, 1, ldata, ct, pad, false); calcLz(ldata, ct, maxm, pad, false); drawFunction(g, viewL, 2, ldata, ct, pad, false); } } int winw = viewX.width; int winh = viewX.height; for (i = 0; i != gridSizeX; i++) for (j = 0; j != gridSizeY; j++) { int x = i*winw/gridSizeX; int y = j*winh/gridSizeY; int x2 = (i+1)*winw/gridSizeX; int y2 = (j+1)*winh/gridSizeY; double cr = func[i][j][0]*colorMult; double cg = func[i][j][1]*colorMult; double cb = func[i][j][2]*colorMult; if (cr == 0 && cg == 0 && cb == 0) { int l; if (mis) for (k = x; k < x2; k++) for (l = y; l < y2; l++) pixels[k+l*viewX.width] = 0xFF000000; continue; } 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)); if (mis) { int l; for (k = x; k < x2; k++) for (l = y; l < y2; l++) pixels[k+l*viewX.width] = colval; } else { g.setColor(new Color(colval)); g.fillRect(x, y+viewX.y, x2-x, y2-y); } } if (mis) { Image dbimage2 = cv.createImage(imageSource); g.drawImage(dbimage2, viewX.x, viewX.y, null); } g.setColor(Color.white); if (sliced) drawCube(g, false); if (axesItem.getState()) drawAxes(g); for (i = 0; i != textCount; i++) { TextBox tb = textBoxes[i]; int h = (tb.height + fontMetrics.getAscent() - fontMetrics.getDescent())/2; g.drawString(tb.text, tb.x, tb.y + h); } g.setColor(Color.yellow); if (selectedState != null) centerString(g, selectedState.getText(), viewX.y+viewX.height-5); if (viewStates != null) drawPhasors(g, viewStates); realg.drawImage(dbimage, 0, 0, this); if (!allQuiet) cv.repaint(pause); } 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() / 2500.; 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 != null && selectedState.elevel == st.elevel); if (viewChooser.getSelectedIndex() >= VIEW_COMBO_N1) 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.fillOval(x+xa-1, y+ya-1, 3, 3); } } 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 = java.lang.Math.sqrt(maxsq); double uncert = java.lang.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 = java.lang.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(); if (e.getSource() == blankButton) doClear(); if (e.getSource() == normalizeButton) normalize(); if (e.getSource() == maximizeButton) maximize(); if (e.getSource() == dispGaussItem) doDispGaussian(); if (e.getSource() == scaled1GaussItem) doScaled1Gaussian(); if (e.getSource() == scaled2GaussItem) doScaled2Gaussian(); if (e.getSource() == rotatingGaussItem) doRotatingGaussian(); if (e.getSource() == dispX110Item) doDispX110(); if (e.getSource() == dispZ110Item) doDispZ110(); if (e.getSource() == measureEItem) measureE(); if (e.getSource() == measureLxItem) measureL(0); if (e.getSource() == measureLyItem) measureL(1); if (e.getSource() == measureLzItem) measureL(2); } void doDispGaussian() { doClear(); int i; rectBasis.convertBasisToDerived(); for (i = 0; i != 5; i++) rectBasis.altStates[i].set(movedGaussian[i], 0); rectBasis.convertDerivedToBasis(); createOrbitals(); } void doScaled1Gaussian() { doClear(); int i; for (i = 0; i != 8; i++) getState(i, 0, 0).set(scaledGaussian[i]); createOrbitals(); } void doScaled2Gaussian() { doClear(); int i; for (i = 0; i != rectBasis.altStateCount; i++) { DerivedState ds = rectBasis.altStates[i]; ds.set(0); if ((ds.nx & 1) > 0 || ds.ny > 0 || (ds.nz & 1) > 0) continue; int s = (ds.nx/2)*3+ds.nz/2; ds.set(scaled2Gaussian[s]); } rectBasis.convertDerivedToBasis(); createOrbitals(); } void doRotatingGaussian() { doClear(); int i; for (i = 0; i != rectBasis.altStateCount; i++) { DerivedState ds = rectBasis.altStates[i]; ds.set(0); int s = ds.nx*3+ds.nz; if (ds.ny > 0 || ds.nx > 2 || ds.nz > 2) continue; ds.set(rotGaussianR[s], rotGaussianI[s]); } rectBasis.convertDerivedToBasis(); createOrbitals(); } void doDispX110() { doClear(); int i; for (i = 0; i != rectBasis.altStateCount; i++) { DerivedState ds = rectBasis.altStates[i]; ds.set(0); if (ds.nz != 1 || ds.ny != 0) continue; ds.set(dispX110Array[ds.nx]); } rectBasis.convertDerivedToBasis(); createOrbitals(); } void doDispZ110() { doClear(); int i; for (i = 0; i != rectBasis.altStateCount; i++) { DerivedState ds = rectBasis.altStates[i]; ds.set(0); //if (ds.nx != 1 || ds.ny != 1) if (ds.nx != 1 || ds.ny != 0) continue; ds.set(dispZ110Array[ds.nz]); } rectBasis.convertDerivedToBasis(); createOrbitals(); } double movedGaussian[] = { // 0.9394130628134758, 0.33213267352531645, 0.08303316838132911 .778801, .550695, .275348, .11241, .039743 }; double scaledGaussian[] = { 0.252982,0.185903,0.124708, 0.0808198,0.0514334,0.0323663, 0.0202127,0.0125533 }; double scaled2Gaussian[] = { 0.923077,0.251044,0.0836194, -0.251044,-0.0682749,-0.0227415, 0.0836194,0.0227415,0.00757488 }; double rotGaussianR[] = { 0.75484,0.,-0.150118,0.400314,0.,-0.079612,0.150118,0.,0 }; double rotGaussianI[] = { 0,0.400314,0,0,0.212299,0,0,0.079612,0 }; double dispX110Array[] = { // -.174036, .953731, .242278, 0, 0 -0.332133,0.821986,0.44035,0.137825, 0 // -0.460759,0.624461,0.559978,0.271215,0.0983688 }; double dispZ110Array[] = { 0.778801,0.550695,0.275348,0.11241,0.039743 }; 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 = java.lang.Math.exp(brightnessBar.getValue()/100.); userBrightMult = mult/bestBrightness; } if (e.getSource() == resolutionBar) setResolution(); setupSimpson(); cv.repaint(pause); } public void mouseDragged(MouseEvent e) { dragging = true; changingDerivedStates = false; 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 = java.lang.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/java.lang.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 = changingDerivedStates = false; } public void itemStateChanged(ItemEvent e) { if (e.getItemSelectable() instanceof CheckboxMenuItem) { setupDisplay(); cv.repaint(pause); return; } if (e.getItemSelectable() == nChooser) { orbitalChanged(); } else if (e.getItemSelectable() == lChooser) { setLValue(); orbitalChanged(); } else if (e.getItemSelectable() == mChooser) { orbitalChanged(); } else if (e.getItemSelectable() == viewChooser) { setLValue(); orbitalChanged(); setupDisplay(); setInitialOrbital(); } 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 = modeChooser.getSelectedIndex(); 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_ROTATE_X) { int xo = dragX-x + dragY-y; rotateXY(xo/40., true); } else if (mode == MODE_ROTATE_Y) { int xo = dragX-x + dragY-y; rotateXY(xo/40., false); } else if (mode == MODE_ROTATE_Z) { int xo = dragX-x + dragY-y; rotateZ(xo/40.); } 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 = java.lang.Math.sqrt(x*x+y*y)/ss2; double ang = java.lang.Math.atan2(-y, x); if (mag > 10) mag = 0; if (mag > 1) mag = 1; selectedState.setMagPhase(mag, ang); if (selectedState instanceof DerivedState) { selectedState.convertDerivedToBasis(); changingDerivedStates = true; } 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 calcLxy(AlternateBasis ab, double data[], int count, int maxm, int pad, boolean xAxis, boolean square) { int i; int mid = count/2; for (i = 0; i != count; i++) data[i] = 0; if (square) mid = 1; // convert to the basis ab.convertBasisToDerived(); int j; double qq = 0; for (j = 0; j != ab.altStateCount; j++) { DerivedState ds = ab.altStates[j]; if (square) data[mid+ds.m*ds.m*pad] += ds.magSquared(); else data[mid+ds.m*pad] += ds.magSquared(); } for (i = 0; i != count; i++) data[i] = java.lang.Math.sqrt(data[i]); } void calcLz(double data[], int count, int maxm, int pad, boolean square) { int i; int mid = count/2; for (i = 0; i != count; i++) data[i] = 0; if (square) mid = 1; for (i = 0; i != stateCount; i++) { BasisState bs = states[i]; if (bs.l <= maxm) { if (square) data[mid+bs.m*bs.m*pad] += bs.magSquared(); else data[mid+bs.m*pad] += bs.magSquared(); } } for (i = 0; i != count; i++) data[i] = java.lang.Math.sqrt(data[i]); } void rotateXY(double ang, boolean xAxis) { AlternateBasis ab = (xAxis) ? lxBasis : lyBasis; // convert to the basis ab.convertBasisToDerived(); // rotate all the states in the basis around the axis int j; for (j = 0; j != ab.altStateCount; j++) { DerivedState ds = ab.altStates[j]; ds.rotate(ang*ds.m); } ab.convertDerivedToBasis(); createOrbitals(); cv.repaint(pause); } void rotateZ(double ang) { int i; for (i = 0; i != stateCount; i++) { BasisState bs = states[i]; bs.rotate(ang*bs.m); } cv.repaint(pause); } void createOrbitals() { int i; int newOrbCount = 0; boolean newOrbitals = false; for (i = 0; i != stateCount; i++) { BasisState st = states[i]; if (st.m == 0) { if (st.mag != 0) { newOrbCount++; if (st.orb == null) newOrbitals = true; } else if (st.orb != null) newOrbitals = true; } else if (st.m > 0) { if (st.mag != 0 || getState(st.nr, st.l, -st.m).mag != 0) { 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 != 0) || (st.m > 0 && (st.mag != 0 || getState(st.nr, st.l, -st.m).mag != 0))) { 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/java.lang.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); } void measureE() { normalize(); double n = random.nextDouble(); int i = 0; int picki = -1; for (i = 0; i != stateCount; i++) { double m = states[i].magSquared(); n -= m; if (n < 0) { picki = i; i = stateCount; break; } } if (picki == -1) return; for (i = 0; i != stateCount; i++) { State st = states[i]; if (st.elevel != states[picki].elevel) st.set(0); } normalize(); } void measureL(int axis) { if (higherStatesPresent()) return; int maxm = 4; int pad = 3; int ct = (maxm*2+1)*pad; double ldata[] = new double[ct]; int mid = ct/2; normalize(); AlternateBasis ab = null; switch (axis) { case 0: calcLxy(ab = lxBasis, ldata, ct, maxm, pad, true, false); break; case 1: calcLxy(ab = lyBasis, ldata, ct, maxm, pad, false, false); break; case 2: calcLz(ldata, ct, maxm, pad, false); break; } double n = random.nextDouble(); int i = 0; int pickm = -100; for (i = -maxm; i <= maxm; i++) { double m = ldata[mid+i*pad]; m *= m; n -= m; if (n < 0) { pickm = i; i = maxm; break; } } if (pickm == -100) return; switch (axis) { case 2: for (i = 0; i != stateCount; i++) { BasisState bs = states[i]; if (bs.m != pickm) bs.set(0); } break; default: for (i = 0; i != ab.altStateCount; i++) { DerivedState ds = ab.altStates[i]; if (ds.m != pickm) ds.set(0); } ab.convertDerivedToBasis(); } maximize(); createOrbitals(); } // this is when we are in single-orbital mode, and the user selects // a different one void orbitalChanged() { if (viewChooser.getSelectedIndex() > VIEW_COMPLEX) return; doClear(); getState(getNR(), getL(), getM()).set(1, 0); createOrbitals(); manualScale = false; } BasisState getState(int nr, int l, int m) { int pre_n_add = nr*(maxl+1)*(maxl+1); int pre_l_add = l*l; /*System.out.println(nr + " " + l + " " + m + " " + (pre_n_add+pre_l_add+l+m));*/ return states[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.nr, st.l, -st.m).magSquared()*normmult; totn += n; avg += n*as; } bestBrightness = 113.9/(java.lang.Math.sqrt(minavg)*totn); double mult = bestBrightness * userBrightMult; int bvalue = (int) (java.lang.Math.log(mult)*100.); brightnessBar.setValue(bvalue); } abstract class Orbital { BasisState state; int n, nr, l, m; double reMult, imMult; Orbital(BasisState bs) { nr = bs.nr; l = bs.l; m = bs.m; n = nr*2+l; state = bs; } void setupFrame(double mult) { reMult = state.re*mult; imMult = state.im*mult; } double 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.); } double getScaleRadius() { // set scale by solving equation Veff(r) = E, assuming m=0 // r^2/2 = (n+3/2) return java.lang.Math.sqrt(2*(n+1.5)); } final int distmult = 4; void precompute() { int x, y, z; dshalf = dataSize/2; double mult = scaleBar.getValue() / 2500.; int mpos = (m < 0) ? -m : m; double lgcorrect = java.lang.Math.pow(-1, m); double norm = radialNorm(nr, l)*sphericalNorm(l, mpos); dataR = new double[dataSize]; for (x = 0; x != dataSize; x++) { double r = x*resadj*mult + .00000001; double rl = java.lang.Math.pow(r, l)*norm; dataR[x] = java.lang.Math.exp(-r*r/2)*rl* hypser(-nr,l+1.5,r*r); } if (l > 0) { dataTh = new double[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] = lgcorrect*plgndr(l, mpos, th); } } if (m != 0) { dataPhiR = new double[8][dataSize+1]; dataPhiI = new double[8][dataSize+1]; for (x = 0; x != 8; x++) for (y = 0; y <= dataSize; y++) { double phi = x*pi/4 + y*(pi/4)/dataSize; dataPhiR[x][y] = java.lang.Math.cos(phi*mpos); dataPhiI[x][y] = java.lang.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 nr, int l) { return java.lang.Math.sqrt(2*factorial(nr)/fracfactorial(l+nr+.5)) * pochhammer(l+1.5, nr)/factorial(nr); } double sphericalNorm(int l, int m) { return java.lang.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; } double fracfactorial(double f) { double res = java.lang.Math.sqrt(pi); while (f > 0) res *= f--; return res; } double pochhammer(double f, int n) { double res = 1; for (; n > 0; n--) { res *= f; f += 1; } 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 { double 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 { double 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.nr, bs.l, -bs.m); } double 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 = java.lang.Math.pow(-1, m); a *= mphase; b *= mphase; f1 = (a+c); f2 = (d-b); f3 = (b+d); f4 = (a-c); } void computePoint(int r, int costh) { try { double q = dataR[r]*dataTh[costh]; double phiValR = dataPhiR[phiSector][phiIndex]; double phiValI = dataPhiI[phiSector][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; void convertDerivedToBasis() {} void convertBasisToDerived() {} void setBasisActive() {} abstract String getText(); } class BasisState extends State { int nr, l, m, n; Orbital orb; String getText() { return "n = " + n + ", nr = " + nr + ", l = " + l + ", m = " + m; } } class DerivedState extends State { int count, m, l, nr, n, nx, ny, nz; AlternateBasis basis; String text; BasisState bstates[]; Complex coefs[]; void convertDerivedToBasis() { basis.convertDerivedToBasis(); } void convertBasisToDerived() { basis.convertBasisToDerived(); } void setBasisActive() { basis.active = true; } String getText() { return text; } } class AlternateBasis { DerivedState altStates[]; int altStateCount; boolean active; int n, l; boolean xAxis; AlternateBasis() { basisList[basisCount++] = this; } void convertDerivedToBasis() { convertDerivedToBasis(true); } void convertDerivedToBasis(boolean clear) { int i, j; if (clear) for (i = 0; i != stateCount; i++) states[i].set(0); Complex c = new Complex(); for (i = 0; i != altStateCount; i++) { DerivedState ds = altStates[i]; for (j = 0; j != ds.count; j++) { c.set(ds.coefs[j]); c.conjugate(); c.mult(ds); ds.bstates[j].add(c); } } double maxm = 0; for (i = 0; i != stateCount; i++) if (states[i].mag > maxm) maxm = states[i].mag; if (maxm > 1) { double mult = 1/maxm; for (i = 0; i != stateCount; i++) states[i].mult(mult); } } void convertBasisToDerived() { int i, j; Complex c1 = new Complex(); Complex c2 = new Complex(); double maxm = 0; for (i = 0; i != altStateCount; i++) { DerivedState ds = altStates[i]; c1.set(0); try { for (j = 0; j != ds.count; j++) { c2.set(ds.coefs[j]); c2.mult(ds.bstates[j]); c1.add(c2); } } catch (Exception e) { System.out.print("Exception at " + i + "\n"); } if (c1.mag < epsilon) c1.set(0); ds.set(c1); if (c1.mag > maxm) maxm = ds.mag; } if (maxm > 1) { double mult = 1/maxm; for (i = 0; i != altStateCount; i++) altStates[i].mult(mult); } } } 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 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 mult(Complex c) { mult(c.re, c.im); } void setMagPhase() { mag = java.lang.Math.sqrt(re*re+im*im); phase = java.lang.Math.atan2(im, re); } void setMagPhase(double m, double ph) { mag = m; phase = ph; re = m*java.lang.Math.cos(ph); im = m*java.lang.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 || java.lang.Math.abs(x) > 1.0) { System.out.print("bad arguments in plgndr\n"); } pmm=1.0; if (m > 0) { somx2=java.lang.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(double a, double 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[]; } class TextBox extends Rectangle { TextBox(int x, int y, int a, int b, String s) { super(x, y, a, b); text = s; } String text; } }