// EMWave2.java (c) 2002 by Paul Falstad, www.falstad.com import java.io.InputStream; import java.awt.*; import java.awt.image.*; import java.applet.Applet; 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.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.Math; import java.awt.event.*; class EMWave2Canvas extends Canvas { EMWave2Frame pg; EMWave2Canvas(EMWave2Frame p) { pg = p; } public Dimension getPreferredSize() { return new Dimension(300,400); } public void update(Graphics g) { pg.updateEMWave2(g); } public void paint(Graphics g) { pg.updateEMWave2(g); } }; class EMWave2Layout implements LayoutManager { public EMWave2Layout() {} 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) { Insets insets = target.insets(); int targetw = target.size().width - insets.left - insets.right; int cw = targetw* 2/3; int targeth = target.size().height - (insets.top+insets.bottom); target.getComponent(0).move(insets.left, insets.top); target.getComponent(0).resize(cw, targeth); int barwidth = targetw - cw; cw += insets.left; int i; 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 EMWave2 extends Applet implements ComponentListener { static EMWave2Frame 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 EMWave2Frame(null); ogf.init(); } void showFrame() { if (ogf == null) { started = true; ogf = new EMWave2Frame(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 EMWave2Frame extends Frame implements ComponentListener, ActionListener, AdjustmentListener, MouseMotionListener, MouseListener, ItemListener { Thread engine = null; Dimension winSize; Image dbimage; Random random; int gridSizeX; int gridSizeY; int gridSizeXY; int windowWidth = 50; int windowHeight = 50; int windowOffsetX = 0; int windowOffsetY = 0; public static final int sourceRadius = 7; public static final double freqMult = .0233333/2; public String getAppletInfo() { return "EMWave2 by Paul Falstad"; } Button clearButton; Button ClearAllButton; Checkbox stoppedCheck; Choice modeChooser; Choice viewChooser; Choice sourceChooser; Choice setupChooser; Vector setupList; Setup setup; Scrollbar speedBar; Scrollbar forceBar; Scrollbar resBar; Scrollbar brightnessBar; Scrollbar lineDensityBar; Scrollbar auxBar; Scrollbar adjustBar; Label auxLabel; Label adjustLabel; double forceTimeZero; double sourceMult; static final double pi = 3.14159265358979323846; OscElement grid[]; int gw; OscSource sources[]; static final int MODE_PERF_CONDUCTOR = 0; static final int MODE_GOOD_CONDUCTOR = 1; static final int MODE_FAIR_CONDUCTOR = 2; static final int MODE_J_POS = 3; static final int MODE_J_NEG = 4; static final int MODE_FERROMAG = 5; static final int MODE_DIAMAG = 6; static final int MODE_MEDIUM = 7; static final int MODE_M_DOWN = 8; static final int MODE_M_UP = 9; static final int MODE_M_LEFT = 10; static final int MODE_M_RIGHT = 11; static final int MODE_RESONANT = 12; static final int MODE_CLEAR = 13; static final int MODE_ADJUST = 14; // same as next one static final int MODE_ADJ_CONDUCT = 14; static final int MODE_ADJ_PERM = 15; static final int MODE_ADJ_J = 16; static final int MODE_ADJ_MEDIUM = 17; static final int MODE_ADJ_MAG_DIR = 18; static final int MODE_ADJ_MAG_STR = 19; static final int VIEW_E = 0; static final int VIEW_B = 1; static final int VIEW_B_LINES = 2; static final int VIEW_B_STRENGTH = 3; static final int VIEW_J = 4; static final int VIEW_E_B = 5; static final int VIEW_E_B_LINES = 6; static final int VIEW_E_B_J = 7; static final int VIEW_E_B_LINES_J = 8; static final int VIEW_H = 9; static final int VIEW_M = 10; static final int VIEW_TYPE = 11; static final int VIEW_A = 12; static final int VIEW_POYNTING = 13; static final int VIEW_ENERGY = 14; static final int VIEW_POYNTING_ENERGY = 15; static final int VIEW_FORCE = 16; static final int VIEW_EFF_CUR = 17; static final int VIEW_MAG_CHARGE = 18; static final int VIEW_CURL_E = 19; static final int VIEW_BX = 20; static final int VIEW_BY = 21; static final int VIEW_HX = 22; static final int VIEW_HY = 23; static final int VIEW_NONE = -1; static final int TYPE_CONDUCTOR = 1; static final int TYPE_DIAMAGNET = 2; static final int TYPE_FERROMAGNET = 3; static final int TYPE_MAGNET = 4; static final int TYPE_CURRENT = 5; static final int TYPE_MEDIUM = 6; static final int TYPE_NONE = 0; // this fudge factor was found by trial and error static final double mhmult = 12; int dragX, dragY; int selectedSource; int forceBarValue; boolean dragging; boolean dragClear; boolean dragSet; double t; int pause; MemoryImageSource imageSource; int pixels[]; int sourceCount = -1; boolean sourcePlane = false; int sourceFreqCount = -1; int sourceWaveform = SWF_SIN; int auxFunction; int adjustSelectX1, adjustSelectY1, adjustSelectX2, adjustSelectY2; static final int mediumMax = 191; static final double mediumMaxIndex = .5; static final int SWF_SIN = 0; static final int SWF_PACKET = 1; static final int AUX_NONE = 0; static final int AUX_PHASE = 1; static final int AUX_FREQ = 2; static final int AUX_SPEED = 3; static final int SRC_NONE = 0; static final int SRC_1S1F = 1; static final int SRC_2S1F = 3; static final int SRC_2S2F = 4; static final int SRC_4S1F = 6; static final int SRC_1S1F_PACKET = 7; static final int SRC_1S1F_PLANE = 8; static final int SRC_2S1F_PLANE = 10; static final int SRC_1S1F_PLANE_PACKET = 12; int getrand(int x) { int q = random.nextInt(); if (q < 0) q = -q; return q % x; } EMWave2Canvas cv; EMWave2 applet; EMWave2Frame(EMWave2 a) { super("TM Electrodynamics Applet v1.4b"); applet = a; } boolean useBufferedImage = false; public void init() { String jv = System.getProperty("java.class.version"); double jvf = new Double(jv).doubleValue(); if (jvf >= 48) useBufferedImage = true; setupList = new Vector(); Setup s = new SingleSourceSetup(); int i = 0; while (s != null) { setupList.addElement(s); s = s.createNext(); if (i++ > 300) { System.out.print("setup loop?\n"); return; } } String os = System.getProperty("os.name"); sources = new OscSource[4]; setLayout(new EMWave2Layout()); cv = new EMWave2Canvas(this); cv.addComponentListener(this); cv.addMouseMotionListener(this); cv.addMouseListener(this); add(cv); setupChooser = new Choice(); for (i = 0; i != setupList.size(); i++) setupChooser.add("Setup: " + ((Setup) setupList.elementAt(i)).getName()); setup = (Setup) setupList.elementAt(0); setupChooser.addItemListener(this); add(setupChooser); sourceChooser = new Choice(); sourceChooser.add("No Sources"); sourceChooser.add("1 Src, 1 Freq"); sourceChooser.add("1 Src, 2 Freq"); sourceChooser.add("2 Src, 1 Freq"); sourceChooser.add("2 Src, 2 Freq"); sourceChooser.add("3 Src, 1 Freq"); sourceChooser.add("4 Src, 1 Freq"); sourceChooser.add("1 Src, 1 Freq (Packet)"); sourceChooser.add("1 Plane Src, 1 Freq"); sourceChooser.add("1 Plane Src, 2 Freq"); sourceChooser.add("2 Plane Src, 1 Freq"); sourceChooser.add("2 Plane Src, 2 Freq"); sourceChooser.add("1 Plane 1 Freq (Packet)"); sourceChooser.select(SRC_1S1F); sourceChooser.addItemListener(this); add(sourceChooser); modeChooser = new Choice(); modeChooser.add("Mouse = Add Perf. Conductor"); modeChooser.add("Mouse = Add Good Conductor"); modeChooser.add("Mouse = Add Fair Conductor"); modeChooser.add("Mouse = Add Current (+)"); modeChooser.add("Mouse = Add Current (-)"); modeChooser.add("Mouse = Add Ferromagnet"); modeChooser.add("Mouse = Add Diamagnet"); modeChooser.add("Mouse = Add Dielectric"); modeChooser.add("Mouse = Add Magnet (Down)"); modeChooser.add("Mouse = Add Magnet (Up)"); modeChooser.add("Mouse = Add Magnet (Left)"); modeChooser.add("Mouse = Add Magnet (Right)"); modeChooser.add("Mouse = Add Resonant Medium"); modeChooser.add("Mouse = Clear"); modeChooser.add("Mouse = Adjust Conductivity"); modeChooser.add("Mouse = Adjust Permeability"); modeChooser.add("Mouse = Adjust Current"); modeChooser.add("Mouse = Adjust Dielectric"); modeChooser.add("Mouse = Adjust Mag Dir"); modeChooser.add("Mouse = Adjust Mag Strength"); modeChooser.addItemListener(this); add(modeChooser); viewChooser = new Choice(); viewChooser.add("Show Electric Field (E)"); viewChooser.add("Show Magnetic Field (B)"); viewChooser.add("Show B Lines"); viewChooser.add("Show B Strength"); viewChooser.add("Show Current (j)"); viewChooser.add("Show E/B"); viewChooser.add("Show E/B lines"); viewChooser.add("Show E/B/j"); viewChooser.add("Show E/B lines/j"); viewChooser.add("Show Mag. Intensity (H)"); viewChooser.add("Show Magnetization (M)"); viewChooser.add("Show Material Type"); viewChooser.add("Show Vec. Potential"); viewChooser.add("Show Poynting Vector"); viewChooser.add("Show Energy Density"); viewChooser.add("Show Poynting/Energy"); viewChooser.add("Show Force"); viewChooser.add("Show Effective Current"); viewChooser.add("Show Magnetic Charge"); viewChooser.add("Show Curl E"); viewChooser.add("Show Bx"); viewChooser.add("Show By"); viewChooser.add("Show Hx"); viewChooser.add("Show Hy"); viewChooser.addItemListener(this); add(viewChooser); viewChooser.select(VIEW_E_B_J); add(clearButton = new Button("Clear Fields")); clearButton.addActionListener(this); add(ClearAllButton = new Button("Clear All")); ClearAllButton.addActionListener(this); stoppedCheck = new Checkbox("Stopped"); stoppedCheck.addItemListener(this); add(stoppedCheck); add(new Label("Simulation Speed", Label.CENTER)); add(speedBar = new Scrollbar(Scrollbar.HORIZONTAL, 180, 1, 1, 2000)); speedBar.addAdjustmentListener(this); add(new Label("Resolution", Label.CENTER)); add(resBar = new Scrollbar(Scrollbar.HORIZONTAL, 40, 5, 16, 140)); resBar.addAdjustmentListener(this); setResolution(); add(new Label("Source Frequency", Label.CENTER)); add(forceBar = new Scrollbar(Scrollbar.HORIZONTAL, forceBarValue = 10, 1, 1, 40)); forceBar.addAdjustmentListener(this); add(new Label("Brightness", Label.CENTER)); add(brightnessBar = new Scrollbar(Scrollbar.HORIZONTAL, 10, 1, 1, 2000)); brightnessBar.addAdjustmentListener(this); add(new Label("Line Density", Label.CENTER)); add(lineDensityBar = new Scrollbar(Scrollbar.HORIZONTAL, 50, 1, 10, 100)); lineDensityBar.addAdjustmentListener(this); add(auxLabel = new Label("", Label.CENTER)); add(auxBar = new Scrollbar(Scrollbar.HORIZONTAL, 1, 1, 1, 40)); auxBar.addAdjustmentListener(this); add(adjustLabel = new Label("", Label.CENTER)); add(adjustBar = new Scrollbar(Scrollbar.HORIZONTAL, 50, 1, 0, 102)); adjustBar.addAdjustmentListener(this); add(new Label("http://www.falstad.com")); try { String param = applet.getParameter("PAUSE"); if (param != null) pause = Integer.parseInt(param); } catch (Exception e) { } random = new Random(); reinit(); setup = (Setup) setupList.elementAt(0); cv.setBackground(Color.black); cv.setForeground(Color.lightGray); setModeChooser(); resize(660, 500); handleResize(); Dimension x = getSize(); Dimension screen = getToolkit().getScreenSize(); setLocation((screen.width - x.width)/2, (screen.height - x.height)/2); show(); } void reinit() { sourceCount = -1; adjustSelectX1 = -1; grid = new OscElement[gridSizeXY]; int i; for (i = 0; i != gridSizeXY; i++) grid[i] = new OscElement(); doSetup(); } void setDamping() { int i, j; for (i = 0; i != gridSizeXY; i++) { grid[i].damp = 1; // need this to avoid reflections in Dielectric setup if (grid[i].medium > 0) grid[i].damp = .99; } for (i = 0; i != windowOffsetX; i++) for (j = 0; j != gridSizeX; j++) { double da = Math.exp(-(windowOffsetX-i)*.002); grid[i+j*gw].damp = grid[gridSizeX-1-i+gw*j].damp = grid[j+i*gw].damp = grid[j+gw*(gridSizeY-1-i)].damp = da; } } void handleResize() { Dimension d = winSize = cv.getSize(); if (winSize.width == 0) return; 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)dbimage). 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 }); dbimage = (Image) cstr.newInstance(new Object[] { new Integer(d.width), new Integer(d.height), new Integer(BufferedImage.TYPE_INT_RGB)}); Method m = biclass.getMethod("getRaster", null); Object ras = m.invoke(dbimage, 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[d.width*d.height]; int i; for (i = 0; i != d.width*d.height; i++) pixels[i] = 0xFF000000; imageSource = new MemoryImageSource(d.width, d.height, pixels, 0, d.width); imageSource.setAnimated(true); imageSource.setFullBufferUpdates(true); dbimage = cv.createImage(imageSource); } } public boolean handleEvent(Event ev) { if (ev.id == Event.WINDOW_DESTROY) { applet.destroyFrame(); return true; } return super.handleEvent(ev); } void doClear() { int x, y; // I set all the elements in the grid to 1e-10 instead of 0 because // if I set them to zero, then the simulation slows down for a // short time until the grid fills up again. Don't ask me why! // I don't know. This showed up when I started using floats // instead of doubles. for (x = 0; x < gridSizeXY; x++) { grid[x].az = grid[x].dazdt = 1e-10; grid[x].epos = 0; if (grid[x].resonant) grid[x].jz = 0; } t = 0; doFilter(); } void doClearAll() { int x, y; for (x = 0; x < gridSizeXY; x++) { OscElement oe = grid[x]; oe.jz = 0; oe.az = oe.dazdt = 1e-10f; oe.boundary = false; oe.gray = false; oe.resonant = false; oe.conductivity = 0; oe.perm = 1; oe.medium = 0; oe.mx = oe.my = 0; oe.epos = 0; } setDamping(); sourceChooser.select(SRC_NONE); setSources(); } void calcBoundaries() { int x, y; int bound = 0; // if walls are in place on border, need to extend that through // hidden area to avoid "leaks" for (x = 0; x < gridSizeX; x++) for (y = 0; y < windowOffsetY; y++) { grid[x+gw*y].conductivity = grid[x+gw*windowOffsetY].conductivity; grid[x+gw*(gridSizeY-y-1)].conductivity = grid[x+gw*(gridSizeY-windowOffsetY-1)].conductivity; } for (y = 0; y < gridSizeY; y++) for (x = 0; x < windowOffsetX; x++) { grid[x+gw*y].conductivity = grid[windowOffsetX+gw*y].conductivity; grid[gridSizeX-x-1+gw*y].conductivity = grid[gridSizeX-windowOffsetX-1+gw*y].conductivity; } for (x = 1; x < gridSizeX-1; x++) for (y = 1; y < gridSizeY-1; y++) { int gi = x+gw*y; OscElement oe = grid[gi]; double perm = oe.perm; int medium = oe.medium; double mx = oe.mx; double my = oe.my; OscElement e1 = grid[gi-1]; OscElement e2 = grid[gi+1]; OscElement e3 = grid[gi-gw]; OscElement e4 = grid[gi+gw]; oe.gray = (oe.conductivity > 0 || oe.medium != 0 || oe.perm != 1 || oe.mx != 0 || oe.my != 0 || oe.resonant); // mark all grid squares where the permeability, medium, // or magnetization is different from one of the neighbors if (e1.perm != perm || e2.perm != perm || e3.perm != perm || e4.perm != perm || e1.medium != medium || e2.medium != medium || e3.medium != medium || e4.medium != medium || e1.mx != mx || e2.mx != mx || e3.mx != mx || e4.mx != mx || e1.my != my || e2.my != my || e3.my != my || e4.my != my || oe.resonant) { oe.boundary = true; bound++; } else oe.boundary = false; } } int getPanelHeight() { return winSize.height / 3; } void centerString(Graphics g, String s, int y) { FontMetrics fm = g.getFontMetrics(); g.drawString(s, (winSize.width-fm.stringWidth(s))/2, y); } public void paint(Graphics g) { cv.repaint(); } long lastTime = 0; public void updateEMWave2(Graphics realg) { if (winSize == null || winSize.width == 0) { // this works around some weird bug in IE which causes the // applet to not show up properly the first time after // a reboot. handleResize(); return; } double tadd = 0; if (!stoppedCheck.getState()) { int val = 5; tadd = val*.05; } int i, j; boolean stopFunc = dragging; if (stoppedCheck.getState()) stopFunc = true; double speedValue = speedBar.getValue()/2.; if (stopFunc) lastTime = 0; else { if (lastTime == 0) lastTime = System.currentTimeMillis(); if (speedValue*(System.currentTimeMillis()-lastTime) < 1000) stopFunc = true; } if (!stopFunc) { int iter; int mxx = gridSizeX-1; int mxy = gridSizeY-1; for (iter = 1; ; iter++) { doSources(tadd, false); setup.doStep(); double sinhalfth = 0; double sinth = 0; double scaleo = 0; double tadd2 = tadd; double forcecoef = 1; int curMedium = 0; OscElement oew, oee, oen, oes, oe; double previ, nexti, prevj, nextj, basis, a, b; System.out.println("forcecoef " + forcecoef + " tadd2 " + (tadd*tadd)); for (j = 1; j != mxy; j++) { int gi = j*gw+1; int giEnd = gi+mxx-1; oe = grid[gi-1]; oee = grid[gi]; for (; gi != giEnd; gi++) { oew = oe; oe = oee; oee = grid[gi+1]; oen = grid[gi-gw]; oes = grid[gi+gw]; if (oe.conductivity > 0) oe.jz = 0; if (oe.boundary) { // here we may be on the boundary between two // media, so we have to do some extra work if (oe.resonant) { oe.jz = oe.jz*.999 + -oe.dazdt * .001 - oe.epos * .02; oe.epos += oe.jz * .2; } if (curMedium != oe.medium) { curMedium = oe.medium; forcecoef = (1- (mediumMaxIndex/mediumMax)*curMedium); forcecoef *= forcecoef; } double az = oe.az; previ = (oew.az-az)/oew.perm; nexti = (oee.az-az)/oee.perm; prevj = (oen.az-az)/oen.perm; nextj = (oes.az-az)/oes.perm; basis = (nexti+previ+nextj+prevj)*.25; // calculate effective current double jz = oew.my - oee.my + oes.mx - oen.mx + oe.jz; a = oe.perm*basis + jz; } else { // easy way previ = oew.az; nexti = oee.az; prevj = oen.az; nextj = oes.az; basis = (nexti+previ+nextj+prevj)*.25; a = oe.jz - (oe.az - basis); } oe.dazdt = (oe.dazdt * oe.damp) + a * forcecoef; } } tadd2 = tadd*tadd; for (j = 1; j != mxy; j++) { int gi = j*gw+1; int giEnd = gi-1+mxx; for (; gi != giEnd; gi++) { oe = grid[gi]; if (oe.conductivity > 0) { a = -oe.dazdt*oe.conductivity; oe.jz = a; oe.dazdt += a; } oe.az += oe.dazdt * tadd2; } } t += tadd; filterGrid(); long tm = System.currentTimeMillis(); if (tm-lastTime > 200 || iter*1000 >= speedValue*(tm-lastTime)) { lastTime = tm; break; } } } renderGrid(); int intf = (gridSizeY/2-windowOffsetY)*winSize.height/windowHeight; for (i = 0; i < sourceCount; i++) { OscSource src = sources[i]; int xx = src.getScreenX(); int yy = src.getScreenY(); plotSource(i, xx, yy); } if (adjustSelectX1 != -1) { int c = getrand(255); int col = 0x010100 * c + 0xFF000000; int lx1 = (int) (adjustSelectX1*winSize.width /windowWidth); int ly1 = (int) (adjustSelectY1*winSize.height/windowHeight); int lx2 = (int) ((adjustSelectX2+1)*winSize.width /windowWidth); int ly2 = (int) ((adjustSelectY2+1)*winSize.height/windowHeight); plotRect(lx1, ly1, lx2-1, ly2-1, col); } if (imageSource != null) imageSource.newPixels(); realg.drawImage(dbimage, 0, 0, this); if (!stoppedCheck.getState()) cv.repaint(pause); } void plotRect(int x1, int y1, int x2, int y2, int col) { int i; for (i = x1; i <= x2; i++) { plotPixel(i, y1, col); plotPixel(i, y2, col); } for (i = y1; i <= y2; i++) { plotPixel(x1, i, col); plotPixel(x2, i, col); } } void plotPixel(int x, int y, int pix) { if (x < 0 || x >= winSize.width) return; try { pixels[x+y*winSize.width] = pix; } catch (Exception e) {} } // draw a circle the slow and dirty way void plotSource(int n, int xx, int yy) { int rad = sourceRadius; int j; int col = (n == selectedSource) ? 0xFF00FFFF : 0xFFFFFFFF; for (j = 0; j <= rad; j++) { int k = (int) (Math.sqrt(rad*rad-j*j)+.5); plotPixel(xx+j, yy+k, col); plotPixel(xx+k, yy+j, col); plotPixel(xx+j, yy-k, col); plotPixel(xx-k, yy+j, col); plotPixel(xx-j, yy+k, col); plotPixel(xx+k, yy-j, col); plotPixel(xx-j, yy-k, col); plotPixel(xx-k, yy-j, col); plotPixel(xx, yy+j, col); plotPixel(xx, yy-j, col); plotPixel(xx+j, yy, col); plotPixel(xx-j, yy, col); } } void renderGrid() { double mult = brightnessBar.getValue() / 50.0; double emult = 1; int ix = 0; int i, j, k, l; int viewScalar, viewVector, viewScalarCond, viewVectorCond; int v = viewChooser.getSelectedIndex(); if (v == VIEW_FORCE) calcForce(); boolean showLines = false; viewScalar = viewScalarCond = viewVector = viewVectorCond = VIEW_NONE; switch (v) { case VIEW_E: case VIEW_A: case VIEW_J: case VIEW_BX: case VIEW_BY: case VIEW_HX: case VIEW_HY: case VIEW_EFF_CUR: case VIEW_MAG_CHARGE: case VIEW_TYPE: case VIEW_B_STRENGTH: case VIEW_ENERGY: viewScalar = viewScalarCond = v; break; case VIEW_B: case VIEW_POYNTING: case VIEW_H: case VIEW_M: case VIEW_CURL_E: viewVector = viewVectorCond = v; break; case VIEW_B_LINES: showLines = true; break; case VIEW_FORCE: viewScalar = viewScalarCond = VIEW_E; viewVector = viewVectorCond = VIEW_FORCE; break; case VIEW_E_B: viewScalar = viewScalarCond = VIEW_E; viewVector = viewVectorCond = VIEW_B; emult = .3; break; case VIEW_E_B_J: viewScalar = VIEW_E; viewScalarCond = VIEW_J; viewVector = viewVectorCond = VIEW_B; emult = .3; break; case VIEW_E_B_LINES: viewScalar = viewScalarCond = VIEW_E; showLines = true; emult = .3; break; case VIEW_E_B_LINES_J: viewScalar = VIEW_E; viewScalarCond = VIEW_J; showLines = true; emult = .3; break; case VIEW_POYNTING_ENERGY: viewScalar = viewScalarCond = VIEW_ENERGY; viewVector = viewVectorCond = VIEW_POYNTING; break; } for (j = 0; j != windowHeight; j++) { ix = winSize.width*(j*winSize.height/windowHeight); int gi = (j+windowOffsetY)*gw+windowOffsetX; for (i = 0; i != windowWidth; i++, gi++) { int x = i*winSize.width/windowWidth; int y = j*winSize.height/windowHeight; int x2 = (i+1)*winSize.width/windowWidth; int y2 = (j+1)*winSize.height/windowHeight; int i2 = i+windowOffsetX; int j2 = j+windowOffsetY; int vs = viewScalar; int vv = viewVector; int col_r = 0, col_g = 0, col_b = 0; OscElement oe = grid[gi]; if (oe.gray || oe.jz != 0) { col_r = col_g = col_b = 64; if (oe.conductivity > 0 || (oe.jz != 0 && !oe.resonant)) { vv = viewVectorCond; vs = viewScalarCond; } } if (vs != VIEW_NONE) { double dy = 0; switch (vs) { case VIEW_E: dy = -oe.dazdt * emult; break; case VIEW_A: dy = oe.az * .2; break; case VIEW_J: dy = oe.jz; break; case VIEW_BX: dy = grid[gi-gw].az - grid[gi+gw].az; break; case VIEW_BY: dy = -(grid[gi+1].az - grid[gi-1].az); break; case VIEW_HX: dy = ((grid[gi-gw].az - grid[gi+gw].az)/oe.perm - oe.mx*mhmult); break; case VIEW_HY: dy = -((grid[gi+1].az - grid[gi-1].az)/oe.perm - oe.my*mhmult); break; case VIEW_EFF_CUR: dy = getMagY(gi-1) - getMagY(gi+1) + getMagX(gi+gw) - getMagX(gi-gw); break; case VIEW_MAG_CHARGE: dy = grid[gi-1].mx - grid[gi+1].mx + grid[gi-gw].my - grid[gi+gw].my; break; case VIEW_B_STRENGTH: { double dx = grid[gi-gw].az - grid[gi+gw].az; dy = grid[gi+1].az - grid[gi-1].az; dy = Math.sqrt(dx*dx+dy*dy); break; } case VIEW_ENERGY: { double n = 1/ (1-oe.medium*mediumMaxIndex/mediumMax); double dielec = n*n; double dx = grid[gi-gw].az - grid[gi+gw].az; dy = grid[gi+1].az - grid[gi-1].az; dy = .4*((dx*dx+dy*dy)/oe.perm + oe.dazdt * oe.dazdt * dielec); break; } } dy *= mult; if (dy < -1) dy = -1; if (dy > 1) dy = 1; if (vs == VIEW_TYPE) { double dr = 0, dg = 0, db = 0; if (oe.resonant) { dr = 1; dg = .75; db = .5; } else if (oe.perm < 1) { dr = 1-oe.perm; } else if (oe.perm > 1) { dg = (oe.perm-1)/30; } else if (oe.mx != 0 || oe.my != 0) { dr = .53; dg = .27; db = .63; } else if (oe.medium > 0) { dr = oe.medium/(double) mediumMax; dg = dr*.5; } else if (oe.conductivity > 0) { dg = db = oe.conductivity; if (oe.conductivity == 1) dr = 1; } else if (oe.jz > 0) { dr = dg = oe.jz*mult; } else if (oe.jz < 0) { db = -oe.jz*mult; } dr = clamp(dr); dg = clamp(dg); db = clamp(db); col_r = col_r+(int) (dr*(255-col_r)); col_g = col_g+(int) (dg*(255-col_g)); col_b = col_b+(int) (db*(255-col_b)); } else if (vs == VIEW_J || vs == VIEW_EFF_CUR || vs == VIEW_ENERGY) { if (dy < 0) col_b = col_b+(int) (-dy*(255-col_b)); else { col_r = col_r+(int) (dy*(255-col_r)); col_g = col_g+(int) (dy*(255-col_g)); } } else { if (dy < 0) col_r = col_r+(int) (-dy*(255-col_r)); else col_g = col_g+(int) (dy*(255-col_g)); } } int col = (255<<24) | (col_r<<16) | (col_g<<8) | col_b; for (k = 0; k != x2-x; k++, ix++) for (l = 0; l != y2-y; l++) pixels[ix+l*winSize.width] = col; oe.col = col; if (vv != VIEW_NONE) { double dx = 0, dy = 0; switch (vv) { case VIEW_B: dx = grid[gi-gw].az - grid[gi+gw].az; dy = grid[gi+1].az - grid[gi-1].az; break; case VIEW_H: dx = (grid[gi-gw].az - grid[gi+gw].az)/oe.perm - mhmult*oe.mx; dy = (grid[gi+1].az - grid[gi-1].az)/oe.perm - mhmult*oe.my; break; case VIEW_M: { double mm = 1-1/oe.perm; dx = (grid[gi-gw].az - grid[gi+gw].az)*mm + oe.mx; dy = (grid[gi+1].az - grid[gi-1].az)*mm + oe.my; } break; case VIEW_POYNTING: dy = 5*oe.dazdt * (grid[gi-gw].az - grid[gi+gw].az)/oe.perm; dx = -5*oe.dazdt * (grid[gi+1].az - grid[gi-1].az)/oe.perm; break; case VIEW_FORCE: dx = forceVecs[forceMap[i2][j2]][0]; dy = forceVecs[forceMap[i2][j2]][1]; break; case VIEW_CURL_E: dx = -5*(grid[gi-gw].dazdt - grid[gi+gw].dazdt); dy = -5*(grid[gi+1].dazdt - grid[gi-1].dazdt); break; } double dn = Math.sqrt(dx*dx+dy*dy); if (dn > 0) { dx /= dn; dy /= dn; } dn *= mult; if (dn > 1) { if (dn > 2) dn = 2; dn -= 1; col_g = 255; col_r = col_r+(int) (dn*(255-col_r)); col_b = col_b+(int) (dn*(255-col_b)); } else col_g = col_g+(int) (dn*(255-col_g)); col = (255<<24) | (col_r<<16) | (col_g<<8) | col_b; int sw2 = (x2-x)/2; int sh2 = (y2-y)/2; int x1 = x+sw2-(int) (sw2*dx); int y1 = y+sh2-(int) (sh2*dy); x2 = x+sw2+(int) (sw2*dx); y2 = y+sh2+(int) (sh2*dy); drawLine(x1, y1, x2, y2, col); int as = 3; drawLine(x2, y2, (int) ( dy*as-dx*as+x2), (int) (-dx*as-dy*as+y2), col); drawLine(x2, y2, (int) (-dy*as-dx*as+x2), (int) ( dx*as-dy*as+y2), col); } } } if (showLines) { renderLines(); lineDensityBar.enable(); } else { lineDensityBar.disable(); } } void drawLine(int x1, int y1, int x2, int y2, int col) { if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0; if (x2 < 0) x2 = 0; if (y2 < 0) y2 = 0; if (x1 >= winSize.width-1) x1 = winSize.width-1; if (y1 >= winSize.height-1) y1 = winSize.height-1; if (x2 >= winSize.width-1) x2 = winSize.width-1; if (y2 >= winSize.height-1) y2 = winSize.height-1; int dx = abs(x2-x1); int dy = abs(y2-y1); if (dx > dy) { if (x1 > x2) { int q; q = x1; x1 = x2; x2 = q; q = y1; y1 = y2; y2 = q; } int x; for (x = x1; x <= x2; x++) pixels[x+(y1+(y2-y1)*(x-x1)/dx)*winSize.width] = col; } else if (dy > 0) { if (y1 > y2) { int q; q = x1; x1 = x2; x2 = q; q = y1; y1 = y2; y2 = q; } int y; for (y = y1; y <= y2; y++) pixels[x1+(x2-x1)*(y-y1)/dy+y*winSize.width] = col; } } double getMagX(int gi) { OscElement oe = grid[gi]; double mm = 1-1/oe.perm; return (grid[gi-gw].az - grid[gi+gw].az)*mm + oe.mx; } double getMagY(int gi) { OscElement oe = grid[gi]; double mm = 1-1/oe.perm; return (grid[gi+1].az - grid[gi-1].az)*mm + oe.my; } double clamp(double x) { return (x < 0) ? 0 : (x > 1) ? 1 : x; } void doSources(double tadd, boolean clear) { int i, j; if (sourceCount == 0) return; double w = forceBar.getValue()*(t-forceTimeZero)*freqMult; double w2 = w; switch (auxFunction) { case AUX_FREQ: w2 = auxBar.getValue()*t*freqMult; break; case AUX_PHASE: { // scrollbars don't always go all the way to the end, // so we do some extra work to make sure we can set // the phase all the way from 0 to pi int au = auxBar.getValue()-1; if (au > 38) au = 38; w2 = w+au*(pi/38); break; } } double v = 0; double v2 = 0; switch (sourceWaveform) { case SWF_SIN: v = Math.sin(w); if (sourceCount >= 2) v2 = Math.sin(w2); else if (sourceFreqCount == 2) v = (v+Math.sin(w2))*.5; break; case SWF_PACKET: { w %= pi*2; double adjw = w/(freqMult*forceBar.getValue()); adjw -= 10; v = Math.exp(-.01*adjw*adjw)* Math.sin(adjw*.2); if (adjw < 0) doFilter(); } break; } if (clear) v = v2 = 0; sources[0].v = sources[2].v = (float) (2*v*sourceMult); sources[1].v = sources[3].v = (float) (2*v2*sourceMult); if (sourcePlane) { for (j = 0; j != sourceCount/2; j++) { OscSource src1 = sources[j*2]; OscSource src2 = sources[j*2+1]; OscSource src3 = sources[j]; drawPlaneSource(src1.x, src1.y, src2.x, src2.y, src3.v*.1); } } else { for (i = 0; i != sourceCount; i++) { OscSource src = sources[i]; OscElement oe = grid[src.x+gw*src.y]; oe.jz = src.v; } } } int abs(int x) { return x < 0 ? -x : x; } void drawPlaneSource(int x1, int y1, int x2, int y2, double v) { if (y1 == y2) { if (x1 == windowOffsetX) x1 = 0; if (x2 == windowOffsetX) x2 = 0; if (x1 == windowOffsetX+windowWidth-1) x1 = gridSizeX-1; if (x2 == windowOffsetX+windowWidth-1) x2 = gridSizeX-1; } if (x1 == x2) { if (y1 == windowOffsetY) y1 = 0; if (y2 == windowOffsetY) y2 = 0; if (y1 == windowOffsetY+windowHeight-1) y1 = gridSizeY-1; if (y2 == windowOffsetY+windowHeight-1) y2 = gridSizeY-1; } // need to draw a line from x1,y1 to x2,y2 if (x1 == x2 && y1 == y2) { grid[x1+gw*y1].jz = v; } else if (abs(y2-y1) > abs(x2-x1)) { // y difference is greater, so we step along y's // from min to max y and calculate x for each step int sgn = sign(y2-y1); int x, y; for (y = y1; y != y2+sgn; y += sgn) { x = x1+(x2-x1)*(y-y1)/(y2-y1); grid[x+gw*y].jz = v; } } else { // x difference is greater, so we step along x's // from min to max x and calculate y for each step int sgn = sign(x2-x1); int x, y; for (x = x1; x != x2+sgn; x += sgn) { y = y1+(y2-y1)*(x-x1)/(x2-x1); grid[x+gw*y].jz = v; } } } int sign(int x) { return (x < 0) ? -1 : (x == 0) ? 0 : 1; } byte linegrid[]; // render electric field lines void renderLines() { double x = 0, y = 0; int lineGridSize = lineDensityBar.getValue(); int lineGridSize2 = lineGridSize*lineGridSize; if (linegrid == null) linegrid = new byte[lineGridSize2]; double lspacing = lineGridSize/(double) windowWidth; double startx = -1, starty = 0; int linemax = 0; double mult = brightnessBar.getValue() / 50.0; boolean doArrow = false; int dir = 1; double olddn = -1; int oldcol = -1; int gridsearchx = 0, gridsearchy = 0; int i, j; for (i = 0; i != lineGridSize2; i++) linegrid[i] = 0; int oldcgx = -1, oldcgy = -1; while (true) { if (linemax-- == 0 || x == 0) { if (dir == 1) { int gi = gridsearchx+lineGridSize*gridsearchy; while (true) { if (linegrid[gi] == 0) break; if (++gridsearchx == lineGridSize) { if (++gridsearchy == lineGridSize) break; gridsearchx = 0; } gi++; } if (gridsearchx == lineGridSize && gridsearchy == lineGridSize) break; startx = gridsearchx/lspacing; starty = gridsearchy/lspacing; } x = startx+.48/lspacing; y = starty+.48/lspacing; linemax = 40; // was 100 dir = -dir; oldcgx = oldcgy = -1; } if (x < 0 || y < 0 || x >= windowWidth || y >= windowHeight) { x = 0; continue; } int cgx = (int) (x*lspacing); int cgy = (int) (y*lspacing); doArrow = true; if (cgx != oldcgx || cgy != oldcgy) { int lg = ++linegrid[cgx+lineGridSize*cgy]; if (lg > 2) { x = 0; continue; } oldcgx = cgx; oldcgy = cgy; } else doArrow = false; int xi = windowOffsetX+(int) x; int yi = windowOffsetY+(int) y; int gi = xi+gw*yi; OscElement oe = grid[gi]; double dx = grid[gi-gw].az - grid[gi+gw].az; double dy = grid[gi+1].az - grid[gi-1].az; double dn = Math.sqrt(dx*dx+dy*dy); if (dn == 0) { x = 0; continue; } dx /= dn; dy /= dn; double oldx = x; double oldy = y; x += .5*dx*dir; y += .5*dy*dir; dn *= mult; int col = grid[gi].col; if (dn != olddn || col != oldcol) { int col_r = (col>>16) & 255; int col_g = (col>> 8) & 255; int col_b = col & 255; if (dn > 1) { if (dn > 2) dn = 2; dn -= 1; col_g = 255; col_r = col_r+(int) (dn*(255-col_r)); col_b = col_b+(int) (dn*(255-col_b)); } else col_g = col_g+(int) (dn*(255-col_g)); col = (255<<24) | (col_r<<16) | (col_g<<8) | col_b; olddn = dn; oldcol = col; } int lx1 = (int) (oldx*winSize.width /windowWidth); int ly1 = (int) (oldy*winSize.height/windowHeight); int lx2 = (int) (x*winSize.width /windowWidth); int ly2 = (int) (y*winSize.height/windowHeight); drawLine(lx1, ly1, lx2, ly2, col); if (doArrow && linegrid[cgx+lineGridSize*cgy] == 1) { if ((cgx & 3) == 0 && (cgy & 3) == 0) { int as = 5; drawLine(lx2, ly2, (int) ( dy*as-dx*as+lx2), (int) (-dx*as-dy*as+ly2), col); drawLine(lx2, ly2, (int) (-dy*as-dx*as+lx2), (int) ( dx*as-dy*as+ly2), col); } } } } byte forceMap[][]; double forceVecs[][]; void calcForce() { int x, y; forceMap = new byte[gridSizeX][gridSizeY]; forceVecs = new double[256][2]; byte magno = 1; for (x = windowOffsetX; x != windowWidth+windowOffsetX; x++) for (y = windowOffsetY; y != windowHeight+windowOffsetY; y++) { if (forceMap[x][y] != 0 || !grid[x+gw*y].feelsForce()) continue; forceVecs[magno][0] = forceVecs[magno][1] = 0; forceSearch(x, y, magno++); } } void forceSearch(int x, int y, byte magno) { if (forceMap[x][y] != 0) return; if (x < windowOffsetX || y < windowOffsetY || x >= windowOffsetX+windowWidth || y >= windowOffsetY+windowHeight) return; int gi = x+y*gw; double mc = getMagX(gi-1) - getMagX(gi+1) + getMagY(gi-gw) - getMagY(gi+gw); double bx = grid[gi-gw].az - grid[gi+gw].az; double by = grid[gi+1].az - grid[gi-1].az; forceVecs[magno][0] += mc*bx + grid[gi].jz*by; forceVecs[magno][1] += mc*by - grid[gi].jz*bx; if (grid[gi].feelsForce()) { forceMap[x][y] = magno; forceSearch(x-1, y, magno); forceSearch(x+1, y, magno); forceSearch(x, y-1, magno); forceSearch(x, y+1, magno); } } int filterCount = 0; // filter out high-frequency noise void filterGrid() { if ((filterCount++ & 3) != 0) return; if (filterCount > 200) return; // filter less aggressively if there is a source on the screen, // to avoid damping the waves double mult1 = (forceBar.getValue() > 7 && sourceCount > 0 && sourceWaveform == SWF_SIN) ? 40 : 8; double mult2 = 4+mult1; int x, y; for (y = 1; y < gridSizeY-1; y++) for (x = 1; x < gridSizeX-1; x++) { int gi = x+y*gw; OscElement oe = grid[gi]; if (oe.jz != 0 || oe.conductivity > 0) continue; if (oe.perm != grid[gi-1].perm || oe.perm != grid[gi+1].perm || oe.perm != grid[gi-gw].perm || oe.perm != grid[gi+gw].perm) continue; double jz = grid[gi-1].my - grid[gi+1].my + grid[gi+gw].mx - grid[gi-gw].mx; if (jz != 0) continue; oe.az = (oe.az*mult1 + grid[gi-1].az + grid[gi+1].az + grid[gi-gw].az + grid[gi+gw].az)/mult2; } } void noFilter() { filterCount = 200; } void doFilter() { filterCount %= 4; } void edit(MouseEvent e) { int x = e.getX(); int y = e.getY(); if (selectedSource != -1) { doSources(1, true); x = x*windowWidth/winSize.width; y = y*windowHeight/winSize.height; OscSource s = sources[selectedSource]; if (x >= 0 && y >= 0 && x < windowWidth && y < windowHeight) { int ox = s.x; int oy = s.y; s.x = x+windowOffsetX; s.y = y+windowOffsetY; cv.repaint(pause); } return; } if (modeChooser.getSelectedIndex() >= MODE_ADJUST) { int xp = x*windowWidth/winSize.width; int yp = y*windowHeight/winSize.height; if (adjustSelectX1 == -1) { adjustSelectX1 = adjustSelectX2 = xp; adjustSelectY1 = adjustSelectY2 = yp; adjustBar.enable(); return; } adjustSelectX1 = max(0, min(xp, adjustSelectX1)); adjustSelectX2 = min(windowWidth-1, max(xp, adjustSelectX2)); adjustSelectY1 = max(0, min(yp, adjustSelectY1)); adjustSelectY2 = min(windowHeight-1, max(yp, adjustSelectY2)); adjustBar.enable(); return; } if (dragX == x && dragY == y) editFuncPoint(x, y); else { // need to draw a line from old x,y to new x,y and // call editFuncPoint for each point on that line. yuck. if (abs(y-dragY) > abs(x-dragX)) { // y difference is greater, so we step along y's // from min to max y and calculate x for each step int x1 = (y < dragY) ? x : dragX; int y1 = (y < dragY) ? y : dragY; int x2 = (y > dragY) ? x : dragX; int y2 = (y > dragY) ? y : dragY; dragX = x; dragY = y; for (y = y1; y <= y2; y++) { x = x1+(x2-x1)*(y-y1)/(y2-y1); editFuncPoint(x, y); } } else { // x difference is greater, so we step along x's // from min to max x and calculate y for each step int x1 = (x < dragX) ? x : dragX; int y1 = (x < dragX) ? y : dragY; int x2 = (x > dragX) ? x : dragX; int y2 = (x > dragX) ? y : dragY; dragX = x; dragY = y; for (x = x1; x <= x2; x++) { y = y1+(y2-y1)*(x-x1)/(x2-x1); editFuncPoint(x, y); } } } calcBoundaries(); cv.repaint(pause); } int min(int a, int b) { return (a < b) ? a : b; } int max(int a, int b) { return (a > b) ? a : b; } void editFuncPoint(int x, int y) { int xp = x*windowWidth/winSize.width; int yp = y*windowHeight/winSize.height; if (xp < 0 || xp >= windowWidth || yp < 0 || yp >= windowHeight) return; xp += windowOffsetX; yp += windowOffsetY; OscElement oe = grid[xp+gw*yp]; doFilter(); if (!dragSet && !dragClear) { dragClear = oe.conductivity != 0 || oe.medium != 0 || oe.mx != 0 || oe.my != 0 || oe.perm != 1 || oe.jz != 0 || oe.resonant; dragSet = !dragClear; } oe.conductivity = 0; oe.medium = 0; oe.mx = oe.my = 0; oe.perm = 1; oe.jz = 0; oe.resonant = false; if (dragClear) return; switch (modeChooser.getSelectedIndex()) { case MODE_J_POS: oe.jz = 1; break; case MODE_J_NEG: oe.jz = -1; break; case MODE_FERROMAG: addPerm(xp, yp, 5); break; case MODE_DIAMAG: addPerm(xp, yp, .5); break; case MODE_MEDIUM: oe.medium = mediumMax; break; case MODE_M_DOWN: oe.my = 1; break; case MODE_M_UP: oe.my = -1; break; case MODE_M_LEFT: oe.mx = -1; break; case MODE_M_RIGHT: oe.mx = 1; break; case MODE_PERF_CONDUCTOR: addConductor(xp, yp, 1); break; case MODE_GOOD_CONDUCTOR: addConductor(xp, yp, .9); break; case MODE_FAIR_CONDUCTOR: addConductor(xp, yp, .5); break; case MODE_RESONANT: oe.resonant = true; break; } } void selectSource(MouseEvent me) { int x = me.getX(); int y = me.getY(); int i; for (i = 0; i != sourceCount; i++) { OscSource src = sources[i]; int sx = src.getScreenX(); int sy = src.getScreenY(); int r2 = (sx-x)*(sx-x)+(sy-y)*(sy-y); if (sourceRadius*sourceRadius > r2) { selectedSource = i; return; } } selectedSource = -1; } 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(100); } public void actionPerformed(ActionEvent e) { if (e.getSource() == clearButton) { doClear(); cv.repaint(); } if (e.getSource() == ClearAllButton) { doClearAll(); cv.repaint(); } } public void adjustmentValueChanged(AdjustmentEvent e) { System.out.print(((Scrollbar) e.getSource()).getValue() + "\n"); if (e.getSource() == resBar) { setResolution(); reinit(); cv.repaint(pause); } if (e.getSource() == brightnessBar) cv.repaint(pause); if (e.getSource() == lineDensityBar) { cv.repaint(pause); linegrid = null; } if (e.getSource() == forceBar) setForce(); if (e.getSource() == adjustBar) doAdjust(); } void setForceBar(int x) { forceBar.setValue(x); forceBarValue = x; forceTimeZero = 0; } void setForce() { // adjust time zero to maintain continuity in the force func // even though the frequency has changed. double oldfreq = forceBarValue * freqMult; forceBarValue = forceBar.getValue(); double newfreq = forceBarValue * freqMult; double adj = newfreq-oldfreq; forceTimeZero = t-oldfreq*(t-forceTimeZero)/newfreq; } void setResolution() { windowWidth = windowHeight = resBar.getValue(); windowOffsetX = windowOffsetY = 20; gridSizeX = windowWidth + windowOffsetX*2; gridSizeY = windowHeight + windowOffsetY*2; gridSizeXY = gridSizeX*gridSizeY; gw = gridSizeX; System.out.println("gridsize " + gridSizeX + " window " + windowWidth); linegrid = null; } void setResolution(int x) { resBar.setValue(x); setResolution(); reinit(); } void doAdjust() { if (adjustSelectX1 == -1) return; int vali = adjustBar.getValue(); if (vali < 1) vali = 1; if (vali > 99) vali = 100; if (modeChooser.getSelectedIndex() == MODE_ADJ_PERM && vali < 3) vali = 3; float val = vali/100.f; int x, y; for (y = adjustSelectY1; y <= adjustSelectY2; y++) for (x = adjustSelectX1; x <= adjustSelectX2; x++) { OscElement oe = grid[x+windowOffsetX+gw*(y+windowOffsetY)]; switch (modeChooser.getSelectedIndex()) { case MODE_ADJ_CONDUCT: if (oe.getType() == TYPE_CONDUCTOR) oe.conductivity = val; break; case MODE_ADJ_PERM: if (oe.getType() == TYPE_FERROMAGNET) oe.perm = vali/2.f; break; case MODE_ADJ_J: if (oe.getType() == TYPE_CURRENT) oe.jz = (oe.jz < 0) ? -val : val; break; case MODE_ADJ_MEDIUM: if (oe.getType() == TYPE_MEDIUM) oe.medium = (int) (val*mediumMax); break; case MODE_ADJ_MAG_DIR: if (oe.getType() == TYPE_MAGNET) { double m = Math.sqrt(oe.mx*oe.mx+oe.my*oe.my); oe.mx = (float) (Math.cos(val*2*pi)*m); oe.my = (float) -(Math.sin(val*2*pi)*m); } break; case MODE_ADJ_MAG_STR: if (oe.getType() == TYPE_MAGNET) { float mult = (float) (val/Math. sqrt(oe.mx*oe.mx+oe.my*oe.my)); oe.mx *= mult; oe.my *= mult; } break; } } calcBoundaries(); } public void mouseDragged(MouseEvent e) { if (!dragging) selectSource(e); dragging = true; edit(e); } public void mouseMoved(MouseEvent e) { if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) return; int x = e.getX(); int y = e.getY(); dragX = x; dragY = y; selectSource(e); } public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { selectedSource = -1; cv.repaint(); } public void mousePressed(MouseEvent e) { if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == 0) return; adjustSelectX1 = -1; adjustBar.disable(); int x = e.getX(); int y = e.getY(); dragX = x; dragY = y; if (!dragging) selectSource(e); dragging = true; edit(e); } public void mouseReleased(MouseEvent e) { if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == 0) return; dragging = false; dragSet = dragClear = false; cv.repaint(); } public void itemStateChanged(ItemEvent e) { cv.repaint(pause); if (e.getItemSelectable() == stoppedCheck) return; if (e.getItemSelectable() == sourceChooser) { setSources(); doFilter(); } if (e.getItemSelectable() == setupChooser) doSetup(); if (e.getItemSelectable() == modeChooser) setModeChooser(); } void setModeChooser() { if (modeChooser.getSelectedIndex() < MODE_ADJUST) { adjustLabel.hide(); adjustBar.hide(); validate(); adjustSelectX1 = -1; return; } switch (modeChooser.getSelectedIndex()) { case MODE_ADJ_CONDUCT: adjustLabel.setText("Conductivity"); break; case MODE_ADJ_PERM: adjustLabel.setText("Permeability"); break; case MODE_ADJ_J: adjustLabel.setText("Current"); break; case MODE_ADJ_MEDIUM: adjustLabel.setText("Dielectric Constant"); break; case MODE_ADJ_MAG_DIR: adjustLabel.setText("Direction"); break; case MODE_ADJ_MAG_STR: adjustLabel.setText("Strength"); break; } adjustLabel.show(); adjustBar.show(); if (adjustSelectX1 == -1) adjustBar.disable(); else adjustBar.enable(); validate(); } void doSetup() { t = 0; doClearAll(); // don't use previous source positions, use defaults sourceCount = -1; filterCount = 0; sourceChooser.select(SRC_1S1F); setForceBar(10); brightnessBar.setValue(100); auxBar.setValue(1); setup = (Setup) setupList.elementAt(setupChooser.getSelectedIndex()); setup.select(); setup.doSetupSources(); calcBoundaries(); setDamping(); } void addMedium() { int i, j; for (i = 0; i != gridSizeX; i++) for (j = gridSizeY/2; j != gridSizeY; j++) grid[i+gw*j].medium = mediumMax; } void addCondMedium(double cv) { conductFillRect(0, gridSizeY/2, gridSizeX-1, gridSizeY-1, cv); } void addResMedium() { int i, j; for (i = 0; i != gridSizeX; i++) for (j = gridSizeY/2; j != gridSizeY; j++) grid[i+gw*j].resonant = true; } void addUniformField() { float v = 2f/windowHeight; int y; for (y = 0; y != gridSizeY; y++) { grid[windowOffsetX+gw*y].jz = v; grid[windowOffsetX+windowWidth-1+gw*y].jz = -v; } } void addSolenoid(int x1, int y1, int x2, int y2, double v) { int i; for (i = y1; i <= y2; i++) { grid[x1+gw*i].jz = v; grid[x2+gw*i].jz = -v; } } void addMagnet(int x1, int y1, int x2, int y2, double v) { int i, j; for (i = y1; i <= y2; i++) for (j = x1; j <= x2; j++) grid[j+gw*i].my = (float) v; } void addMagnet(int x1, int y1, int x2, int y2, double vx, double vy) { int i, j; for (i = y1; i <= y2; i++) for (j = x1; j <= x2; j++) { grid[j+gw*i].mx = (float) vx; grid[j+gw*i].my = (float) vy; } } void setSources() { if (sourceCount > 0) doSources(1, true); sourceMult = 1; int oldSCount = sourceCount; boolean oldPlane = sourcePlane; sourceFreqCount = 1; sourcePlane = (sourceChooser.getSelectedIndex() >= SRC_1S1F_PLANE); sourceWaveform = SWF_SIN; sourceCount = 1; switch (sourceChooser.getSelectedIndex()) { case 0: sourceCount = 0; break; case 2: sourceFreqCount = 2; break; case 3: sourceCount = 2; break; case 4: sourceCount = 2; sourceFreqCount = 2; break; case 5: sourceCount = 3; break; case 6: sourceCount = 4; break; case 7: sourceWaveform = SWF_PACKET; break; case 9: sourceFreqCount = 2; break; case 10: sourceCount = 2; break; case 11: sourceCount = sourceFreqCount = 2; break; case 12: sourceWaveform = SWF_PACKET; break; } if (sourceFreqCount >= 2) { auxFunction = AUX_FREQ; auxBar.setValue(forceBar.getValue()); if (sourceCount == 2) auxLabel.setText("Source 2 Frequency"); else auxLabel.setText("2nd Frequency"); } else if (sourceCount == 2 || sourceCount == 4) { auxFunction = AUX_PHASE; auxBar.setValue(1); auxLabel.setText("Phase Difference"); } else { auxFunction = AUX_NONE; auxBar.hide(); auxLabel.hide(); } if (auxFunction != AUX_NONE) { auxBar.show(); auxLabel.show(); } validate(); if (sourcePlane) { sourceCount *= 2; if (!(oldPlane && oldSCount == sourceCount)) { int x2 = windowOffsetX+windowWidth-1; int y2 = windowOffsetY+windowHeight-1; sources[0] = new OscSource(windowOffsetX, windowOffsetY); sources[1] = new OscSource(x2, windowOffsetY); sources[2] = new OscSource(windowOffsetX, y2); sources[3] = new OscSource(x2, y2); } } else if (!(oldSCount == sourceCount && !oldPlane)) { sources[0] = new OscSource(gridSizeX/2, windowOffsetY+1); sources[1] = new OscSource(gridSizeX/2, gridSizeY-windowOffsetY-2); sources[2] = new OscSource(windowOffsetX+1, gridSizeY/2); sources[3] = new OscSource(gridSizeX-windowOffsetX-2, gridSizeY/2); } } class OscSource { int x; int y; double v; OscSource(int xx, int yy) { x = xx; y = yy; } int getScreenX() { return ((x-windowOffsetX) * winSize.width+winSize.width/2) /windowWidth; } int getScreenY() { return ((y-windowOffsetY) * winSize.height+winSize.height/2) /windowHeight; } }; class OscElement { // permeability of this square (1 = vacuum) float perm; // conductivity (0, vacuum, 1 = perfect) float conductivity; // permanent magnetization float mx, my; // current double jz; // electron position (for resonance) float epos; // damping (used to keep waves from reflecting back after going // off the screen) double damp; // z component of vector potential and its first derivative double az, dazdt; // dielectric strength (0 = none, up to mediumMax) int medium; // temp variable used to store color when drawing field lines int col; // true if we are on a boundary between media boolean boundary; // true if this is a gray square (some medium) boolean gray; // true if this is a resonant medium boolean resonant; int getType() { if (perm < 1) return TYPE_DIAMAGNET; else if (perm > 1) return TYPE_FERROMAGNET; else if (mx != 0 || my != 0) return TYPE_MAGNET; else if (medium > 0) return TYPE_MEDIUM; else if (conductivity > 0) return TYPE_CONDUCTOR; else if (jz != 0) return TYPE_CURRENT; return TYPE_NONE; } boolean feelsForce() { int t = getType(); return (t != TYPE_NONE && t != TYPE_MEDIUM); } }; abstract class Setup { abstract String getName(); void select() {} void deselect() {} void valueChanged(Scrollbar s) {} void doStep() {} void doSetupSources() { setSources(); } abstract Setup createNext(); Setup() { } }; class SingleSourceSetup extends Setup { String getName() { return "Single Source"; } void select() { setForceBar(30); } Setup createNext() { return new DoubleSourceSetup(); } } class DoubleSourceSetup extends Setup { String getName() { return "Two Sources"; } void select() { setForceBar(30); } void doSetupSources() { sourceChooser.select(SRC_2S1F); setSources(); sources[0].y = gridSizeY/2 - 8; sources[1].y = gridSizeY/2 + 8; sources[0].x = sources[1].x = gridSizeX/2; } Setup createNext() { return new PlaneWaveSetup(); } } class PlaneWaveSetup extends Setup { String getName() { return "Plane Wave"; } void select() { sourceChooser.select(SRC_1S1F_PLANE); brightnessBar.setValue(225); setForceBar(30); } Setup createNext() { return new IntersectingPlaneWavesSetup(); } } class IntersectingPlaneWavesSetup extends Setup { String getName() { return "Intersecting Planes"; } void select() { brightnessBar.setValue(70); setForceBar(34); } void doSetupSources() { sourceChooser.select(SRC_2S1F_PLANE); setSources(); sources[0].y = sources[1].y = windowOffsetY; sources[0].x = windowOffsetX+1; sources[2].x = sources[3].x = windowOffsetX; sources[2].y = windowOffsetY+1; sources[3].y = windowOffsetY+windowHeight-1; } Setup createNext() { return new SingleWireSetup(); } } class SingleWireSetup extends Setup { String getName() { return "Single Wire"; } void select() { sourceChooser.select(SRC_NONE); grid[gridSizeX/2+gw*(gridSizeY/2)].jz = 1; brightnessBar.setValue(200); } Setup createNext() { return new DoubleWireSetup(); } } class DoubleWireSetup extends Setup { String getName() { return "Wire Pair"; } void select() { sourceChooser.select(SRC_NONE); grid[gridSizeX/2+gw*(gridSizeY/2-3)].jz = 1; grid[gridSizeX/2+gw*(gridSizeY/2+3)].jz = 1; brightnessBar.setValue(200); } Setup createNext() { return new DipoleWireSetup(); } } class DipoleWireSetup extends Setup { String getName() { return "Dipole Wire Pair"; } void select() { sourceChooser.select(SRC_NONE); grid[gridSizeX/2+gw*(gridSizeY/2-3)].jz = 1; grid[gridSizeX/2+gw*(gridSizeY/2+3)].jz = -1; brightnessBar.setValue(200); } Setup createNext() { return new MagnetPairSetup(); } } class MagnetPairSetup extends Setup { String getName() { return "Magnet Pair"; } void select() { sourceChooser.select(SRC_NONE); addMagnet(gridSizeX/2-windowWidth/4-3, gridSizeY/2-2, gridSizeX/2-windowWidth/4+3, gridSizeY/2+2, -.2); addMagnet(gridSizeX/2+windowWidth/4-3, gridSizeY/2-2, gridSizeX/2+windowWidth/4+3, gridSizeY/2+2, -.2); } Setup createNext() { return new MagnetPairOppSetup(); } } class MagnetPairOppSetup extends Setup { String getName() { return "Magnet Pair, Opp"; } void select() { sourceChooser.select(SRC_NONE); addMagnet(gridSizeX/2-windowWidth/4-3, gridSizeY/2-2, gridSizeX/2-windowWidth/4+3, gridSizeY/2+2, -.2); addMagnet(gridSizeX/2+windowWidth/4-3, gridSizeY/2-2, gridSizeX/2+windowWidth/4+3, gridSizeY/2+2, .2); } Setup createNext() { return new MagnetPairStackedSetup(); } } class MagnetPairStackedSetup extends Setup { String getName() { return "Magnet Pair Stacked"; } void select() { sourceChooser.select(SRC_NONE); addMagnet(gridSizeX/2-3, gridSizeY/2-10, gridSizeX/2+3, gridSizeY/2-5, -.2); addMagnet(gridSizeX/2-3, gridSizeY/2+5, gridSizeX/2+3, gridSizeY/2+10, -.2); } Setup createNext() { return new MagnetPairStackedOppSetup(); } } class MagnetPairStackedOppSetup extends Setup { String getName() { return "Magnet Pair Stacked Opp"; } void select() { sourceChooser.select(SRC_NONE); addMagnet(gridSizeX/2-3, gridSizeY/2-10, gridSizeX/2+3, gridSizeY/2- 5, .2); addMagnet(gridSizeX/2-3, gridSizeY/2+ 5, gridSizeX/2+3, gridSizeY/2+10, -.2); } Setup createNext() { return new UniformFieldSetup(); } } class UniformFieldSetup extends Setup { String getName() { return "Uniform Field"; } void select() { sourceChooser.select(SRC_NONE); addUniformField(); brightnessBar.setValue(225); } Setup createNext() { return new ApertureFieldSetup(); } } class ApertureFieldSetup extends Setup { String getName() { return "Field Near Aperture"; } void select() { sourceChooser.select(SRC_NONE); float v = 2f/windowHeight; int y; for (y = 0; y != gridSizeY; y++) grid[windowOffsetX+gw*y].jz = v; int r1 = gridSizeX/2-windowWidth/6; int r2 = gridSizeX/2+windowWidth/6; conductDrawRect(r1, windowOffsetY, r1, windowOffsetY+windowHeight-1, 1); conductDrawRect(r1, gridSizeY/2-6, r1, gridSizeY/2+6, 0); brightnessBar.setValue(740); } Setup createNext() { return new SolenoidSetup(); } } class SolenoidSetup extends Setup { String getName() { return "Solenoid"; } void select() { sourceChooser.select(SRC_NONE); int h = windowHeight/3; double v = 2./h; int cy = gridSizeY/2; addSolenoid(gridSizeX/2-3, cy-h, gridSizeX/2+3, cy+h, v); } Setup createNext() { return new ToroidalSolenoidSetup(); } } class ToroidalSolenoidSetup extends Setup { String getName() { return "Toroidal Solenoid"; } void select() { sourceChooser.select(SRC_NONE); addWireCircle(gridSizeX/2, gridSizeY/2, windowHeight/3, -30/360., 0, 360); addWireCircle(gridSizeX/2, gridSizeY/2, windowHeight/6, 30/360., 0, 360); brightnessBar.setValue(400); } Setup createNext() { return new CylinderSetup(); } } class CylinderSetup extends Setup { String getName() { return "Sphere"; } void select() { sourceChooser.select(SRC_NONE); int res = 4; int cx = gridSizeX/2 * res; int cy = gridSizeY/2 * res; int r = windowHeight/5 * res; double my = -1./(r*r); int x, y; for (x = -r; x <= r; x++) { int yd = (int) Math.sqrt(r*r-x*x); for (y = -yd; y <= yd; y++) grid[( x+cx)/res+gw*((y+cy)/res)].my += my; } brightnessBar.setValue(450); } Setup createNext() { return new ThickWireSetup(); } } class ThickWireSetup extends Setup { String getName() { return "Thick Wire"; } void select() { int r = windowWidth/4; addThickWire(gridSizeX/2, gridSizeY/2, r, 1./(r*r)); sourceChooser.select(SRC_NONE); } Setup createNext() { return new HoleInWire1Setup(); } } class HoleInWire1Setup extends Setup { String getName() { return "Hole In Wire 1"; } void select() { int r = windowWidth/3; double j = 1./(r*r); // avoid rounding error j = ((int) (j*1024))/1024.; if (j == 0) j = 1/1024.; addThickWire(gridSizeX/2, gridSizeY/2, r, j); addThickWire(gridSizeX/2, gridSizeY/2, r*2/3, -j); sourceChooser.select(SRC_NONE); brightnessBar.setValue(450); } Setup createNext() { return new HoleInWire2Setup(); } } class HoleInWire2Setup extends Setup { String getName() { return "Hole In Wire 2"; } void select() { int r = windowWidth/3; double j = 1./(r*r); // avoid rounding error j = ((int) (j*1024))/1024.; if (j == 0) j = 1/1024.; addThickWire(gridSizeX/2, gridSizeY/2, r, j); addThickWire(gridSizeX/2+r/4, gridSizeY/2, r/2, -j); sourceChooser.select(SRC_NONE); brightnessBar.setValue(450); } Setup createNext() { return new FerromagnetSetup(); } } class FerromagnetSetup extends Setup { String getName() { return "Ferromagnet"; } void select() { sourceChooser.select(SRC_NONE); addSolenoid(gridSizeX/2-2, gridSizeY/2-2, gridSizeX/2+2, gridSizeY/2+2, .4); int x1 = windowOffsetX+3; int x2 = windowOffsetX+windowWidth-4; permFillRect(x1, gridSizeY/2+4, x2, gridSizeY/2+8, 5); brightnessBar.setValue(200); } Setup createNext() { return new DiamagnetSetup(); } } class DiamagnetSetup extends Setup { String getName() { return "Diamagnet"; } void select() { sourceChooser.select(SRC_NONE); addSolenoid(gridSizeX/2-2, gridSizeY/2-2, gridSizeX/2+2, gridSizeY/2+2, .4); int x1 = windowOffsetX+3; int x2 = windowOffsetX+windowWidth-4; permFillRect(x1, gridSizeY/2+4, x2, gridSizeY/2+8, .5); brightnessBar.setValue(200); } Setup createNext() { return new MeissnerEffectSetup(); } } class MeissnerEffectSetup extends Setup { String getName() { return "Meissner Effect"; } void select() { sourceChooser.select(SRC_NONE); addSolenoid(gridSizeX/2-2, gridSizeY/2-2, gridSizeX/2+2, gridSizeY/2+2, .4); int x1 = windowOffsetX+3; int x2 = windowOffsetX+windowWidth-4; conductFillRect(x1, gridSizeY/2+4, x2, gridSizeY/2+8, 1); brightnessBar.setValue(200); } Setup createNext() { return new HorseshoeSetup(); } } class HorseshoeSetup extends Setup { String getName() { return "Horseshoe Magnet"; } void select() { sourceChooser.select(SRC_NONE); int r1 = windowHeight/3; int r2 = windowHeight/6; addWireCircle(gridSizeX/2, gridSizeY/2, r1, -30/360., 0, 180); addWireCircle(gridSizeX/2, gridSizeY/2, r2, 30/360., 0, 180); int y; for (y = 0; y != r2; y++) { int x; for (x = -r1; x <= r1; x++) grid[gridSizeX/2+x+gw*(gridSizeY/2+y)].jz = grid[gridSizeX/2+x+gw*(gridSizeY/2+y-1)].jz; } brightnessBar.setValue(400); } Setup createNext() { return new Horseshoe2Setup(); } } class Horseshoe2Setup extends HorseshoeSetup { String getName() { return "Horseshoe + Load"; } void select() { super.select(); int r1 = windowHeight/3; int r2 = windowHeight/6; permFillRect(gridSizeX/2-r1-3, gridSizeY/2+r2, gridSizeX/2+r1+3, gridSizeY/2+r2*2, 5); brightnessBar.setValue(225); } Setup createNext() { return new MagneticShielding1Setup(); } } class MagneticShielding1Setup extends Setup { String getName() { return "Magnetic Shielding 1"; } void select() { sourceChooser.select(SRC_NONE); int i, j; addSolenoid(gridSizeX/2-2, gridSizeY/2-2, gridSizeX/2+2, gridSizeY/2+2, .4); int x1 = windowOffsetX+3; int x2 = windowOffsetX+windowWidth-4; permDrawRect(x1, gridSizeY/2+4, x2, gridSizeY/2+5, 10); } Setup createNext() { return new MagneticShielding2Setup(); } } class MagneticShielding2Setup extends Setup { String getName() { return "Magnetic Shielding 2"; } void select() { sourceChooser.select(SRC_NONE); int i, j; addSolenoid(gridSizeX/2-2, gridSizeY/2-2, gridSizeX/2+2, gridSizeY/2+2, .4); for (i = 6; i <= 8; i++) permDrawRect(gridSizeX/2-i, gridSizeY/2-i, gridSizeX/2+i, gridSizeY/2+i, 10); } Setup createNext() { return new MagneticShielding3Setup(); } } class MagneticShielding3Setup extends Setup { String getName() { return "Magnetic Shielding 3"; } void select() { sourceChooser.select(SRC_NONE); int cx = gridSizeX/2; int cy = gridSizeY/2; addSolenoid(cx-1, cy-1, cx+1, cy+1, 4); int th; brightnessBar.setValue(340); for (th = 0; th != 360; th += 3) { double r1 = 4.9; int x = cx+(int) (Math.cos(th*pi/180)*r1); int y = cy-(int) (Math.sin(th*pi/180)*r1); addPerm(x, y, 5); double r2 = 5.9; x = cx+(int) (Math.cos(th*pi/180)*r2); y = cy-(int) (Math.sin(th*pi/180)*r2); addPerm(x, y, 5); } } Setup createNext() { return new MagneticShielding4Setup(); } } class MagneticShielding4Setup extends Setup { String getName() { return "Magnetic Shielding 4"; } void select() { sourceChooser.select(SRC_NONE); int i; for (i = 6; i <= 8; i++) permDrawRect(gridSizeX/2-i, gridSizeY/2-i, gridSizeX/2+i, gridSizeY/2+i, 10); addUniformField(); brightnessBar.setValue(250); } Setup createNext() { return new MagneticCircuit1Setup(); } } class MagneticCircuit1Setup extends Setup { String getName() { return "Magnetic Circuit 1"; } void select() { sourceChooser.select(SRC_NONE); int i; for (i = 6; i <= 9; i++) { permDrawRect(gridSizeX/2-i, gridSizeY/2-i, gridSizeX/2+i, gridSizeY/2+i, 10); } addSolenoid(gridSizeX/2+5, gridSizeY/2-1, gridSizeX/2+10, gridSizeY/2+1, .2); } Setup createNext() { return new MagneticCircuit2Setup(); } } class MagneticCircuit2Setup extends MagneticCircuit1Setup { String getName() { return "Magnetic Circuit 2"; } void select() { super.select(); permFillRect(gridSizeX/2-9, gridSizeY/2-1, gridSizeX/2-6, gridSizeY/2+1, 1); } Setup createNext() { return new MonopoleAttemptSetup(); } } class MonopoleAttemptSetup extends Setup { String getName() { return "Monopole Attempt"; } void select() { sourceChooser.select(SRC_NONE); int w = windowWidth/5; int i; int cx = gridSizeX/2; int cy = gridSizeY/2; int j; for (j = 0; j != 3; j++) { for (i = -w+1; i < w; i++) { grid[cx-w+gw*(cy+i)].mx = -1; grid[cx+w+gw*(cy+i)].mx = 1; grid[cx+i+gw*(cy-w)].my = -1; grid[cx+i+gw*(cy+w)].my = 1; } w++; } } Setup createNext() { return new QuadrupoleLensSetup(); } } class QuadrupoleLensSetup extends Setup { String getName() { return "Quadrupole Lens"; } void select() { sourceChooser.select(SRC_NONE); int x; int w = gridSizeX/2-1; int h = windowWidth/4; int cx = gridSizeX/2; int cy = gridSizeY/2; float str = 1/(float) h; for (x = -w; x <= w; x++) { int yd = (int) Math.sqrt(x*x+h*h); int y; for (y = yd; y <= w; y++) { grid[cx+x+gw*(cy+y)].my = -str; grid[cx+x+gw*(cy-y)].my = str; grid[cx+y+gw*(cy+x)].mx = str; grid[cx-y+gw*(cy+x)].mx = -str; } } } Setup createNext() { return new HalbachArraySetup(); } } class HalbachArraySetup extends Setup { String getName() { return "Halbach Array"; } void select() { brightnessBar.setValue(80); sourceChooser.select(SRC_NONE); int sz = 5; int y1 = gridSizeY/2-sz/2; int y2 = gridSizeY/2+sz/2; int fx = gridSizeX/2 -sz/2 -2*sz; int s1 = sz-1; addMagnet(fx, y1, fx+s1, y2, -.2, 0); fx += sz; addMagnet(fx, y1, fx+s1, y2, 0, -.2); fx += sz; addMagnet(fx, y1, fx+s1, y2, .2, 0); fx += sz; addMagnet(fx, y1, fx+s1, y2, 0, .2); fx += sz; addMagnet(fx, y1, fx+s1, y2, -.2, 0); fx += sz; } Setup createNext() { return new HalbachArray2Setup(); } } class HalbachArray2Setup extends Setup { String getName() { return "Halbach Array (long)"; } void select() { brightnessBar.setValue(80); sourceChooser.select(SRC_NONE); int sz = 3; int y1 = gridSizeY/2-1; int y2 = gridSizeY/2+1; int fx = windowOffsetX + (windowWidth-windowWidth/sz*sz)/2; int s1 = sz-1; int i; double dx = -.2; double dy = 0; for (i = 0; i != windowWidth/sz; i++) { addMagnet(fx, y1, fx+s1, y2, dx, dy); fx += sz; double dq = dx; dx = -dy; dy = dq; } } Setup createNext() { return new HalbachArray3Setup(); } } class HalbachArray3Setup extends Setup { String getName() { return "Halbach Array (dipole)"; } void select() { brightnessBar.setValue(47); sourceChooser.select(SRC_NONE); int i; int r2 = windowWidth/3; int r1 = r2/2; int x, y; for (x = -r2; x <= r2; x++) for (y = -r2; y <= r2; y++) { double r = Math.sqrt(x*x+y*y); if (r > r2+.9 || r < r1) continue; double a = Math.atan2(y, x)*180/pi + 22.5 + 45; if (a < 0) a += 360; int ai = (int) (a/45); float dq = ((ai & 2) == 0) ? .2f : -.2f; float dx = 0, dy = 0; if ((ai & 1) == 0) dx = dq; else dy = dq; grid[gridSizeX/2+x+gw*(gridSizeY/2+y)].mx = dx; grid[gridSizeX/2+x+gw*(gridSizeY/2+y)].my = dy; } } Setup createNext() { return new HalbachArray4Setup(); } } class HalbachArray4Setup extends Setup { String getName() { return "Halbach Array (quadrupole)"; } void select() { brightnessBar.setValue(255); sourceChooser.select(SRC_NONE); int i; int r2 = windowWidth/3; int r1 = r2*2/3; int x, y; for (x = -r2; x <= r2; x++) for (y = -r2; y <= r2; y++) { double r = Math.sqrt(x*x+y*y); if (r > r2+.9 || r < r1) continue; double a = Math.atan2(y, x)*180/pi + 11.25; if (a < 0) a += 360; int ai = (int) (a/22.5); double da = -pi/2 + (pi*3/2)*ai/4.; float dx = (float) Math.cos(da); float dy = (float) Math.sin(da); float d0 = -.06f; grid[gridSizeX/2+x+gw*(gridSizeY/2+y)].mx = dx*d0; grid[gridSizeX/2+x+gw*(gridSizeY/2+y)].my = dy*d0; } } Setup createNext() { return new DielectricSetup(); } } class DielectricSetup extends Setup { String getName() { return "Dielectric"; } void select() { sourceChooser.select(SRC_1S1F_PACKET); addMedium(); setForceBar(4); brightnessBar.setValue(1000); noFilter(); } Setup createNext() { return new ConductReflectSetup(); } } class ConductReflectSetup extends Setup { String getName() { return "Fair Conductor Reflection"; } void select() { sourceChooser.select(SRC_1S1F_PACKET); addCondMedium(.5); setForceBar(4); brightnessBar.setValue(800); } Setup createNext() { return new Conduct2ReflectSetup(); } } class Conduct2ReflectSetup extends Setup { String getName() { return "Poor Conductor Reflection"; } void select() { sourceChooser.select(SRC_1S1F_PACKET); addCondMedium(.1); setForceBar(4); brightnessBar.setValue(800); } Setup createNext() { return new SkinEffect1Setup(); } } class SkinEffect1Setup extends Setup { String getName() { return "Skin Effect 1"; } void select() { sourceChooser.select(SRC_1S1F); addCondMedium(.33); setForceBar(6); brightnessBar.setValue(800); } Setup createNext() { return new SkinEffect2Setup(); } } class SkinEffect2Setup extends Setup { String getName() { return "Skin Effect 2"; } void select() { sourceChooser.select(SRC_1S1F); addCondMedium(.33); setForceBar(40); brightnessBar.setValue(800); } Setup createNext() { return new ResonantAbsSetup(); } } class ResonantAbsSetup extends Setup { String getName() { return "Resonant Absorption"; } void select() { addResMedium(); setForceBar(23); brightnessBar.setValue(200); noFilter(); } Setup createNext() { return new Dispersion1Setup(); } } class Dispersion1Setup extends ResonantAbsSetup { String getName() { return "Dispersion 1"; } void select() { super.select(); setForceBar(14); } Setup createNext() { return new Dispersion2Setup(); } } class Dispersion2Setup extends ResonantAbsSetup { String getName() { return "Dispersion 2"; } void select() { super.select(); setForceBar(21); } Setup createNext() { return new Dispersion3Setup(); } } class Dispersion3Setup extends ResonantAbsSetup { String getName() { return "Dispersion 3"; } void select() { super.select(); setForceBar(25); } Setup createNext() { return new Dispersion4Setup(); } } class Dispersion4Setup extends ResonantAbsSetup { String getName() { return "Dispersion 4"; } void select() { super.select(); setForceBar(39); } Setup createNext() { return new DiffusionSetup(); } } class DiffusionSetup extends Setup { String getName() { return "Magnetic Diffusion"; } void select() { sourceChooser.select(SRC_NONE); conductFillRect(1, 1, gridSizeX-2, gridSizeY-2, .2); addConductor(gridSizeX/2, gridSizeY/2, 0); grid[gridSizeX/2+gw*(gridSizeY/2)].jz = 1; brightnessBar.setValue(800); } Setup createNext() { return new OscRingSetup(); } } class OscRingSetup extends Setup { String getName() { return "Oscillating Ring"; } void doSetupSources() { sourceChooser.select(SRC_2S1F); setSources(); sources[0].y = sources[1].y = gridSizeY/2; sources[0].x = gridSizeX/2 - 4; sources[1].x = gridSizeX/2 + 4; auxBar.setValue(40); } void select() { setForceBar(26); brightnessBar.setValue(86); } Setup createNext() { return new OscRingPairSetup(); } } class OscRingPairSetup extends Setup { String getName() { return "Oscillating Ring Pair"; } void doSetupSources() { sourceChooser.select(SRC_4S1F); setSources(); sources[0].y = sources[1].y = gridSizeY/2-2; sources[2].y = sources[3].y = gridSizeY/2+2; sources[0].x = sources[3].x = gridSizeX/2 - 2; sources[1].x = sources[2].x = gridSizeX/2 + 2; auxBar.setValue(40); } void select() { setForceBar(26); brightnessBar.setValue(86); } Setup createNext() { return new OscRingInductionSetup(); } } class OscRingInductionSetup extends Setup { String getName() { return "Ring Induction"; } void doSetupSources() { sourceChooser.select(SRC_2S1F); setSources(); sources[0].y = sources[1].y = gridSizeY/2-2; sources[0].x = gridSizeX/2 - 4; sources[1].x = gridSizeX/2 + 4; auxBar.setValue(40); } void select() { setForceBar(12); brightnessBar.setValue(140); addConductor(gridSizeX/2-4, gridSizeY/2+2, .5); addConductor(gridSizeX/2+4, gridSizeY/2+2, .5); } Setup createNext() { return new WireInductionSetup(); } } class WireInductionSetup extends Setup { String getName() { return "Wire Induction"; } void doSetupSources() { setForceBar(12); sourceChooser.select(SRC_1S1F); setSources(); sources[0].y = gridSizeY/2-2; sources[0].x = gridSizeX/2; } void select() { brightnessBar.setValue(140); addConductor(gridSizeX/2, gridSizeY/2+2, .5); } Setup createNext() { return new OscRingEddy1Setup(); } } class OscRingEddy1Setup extends OscRingSetup { String getName() { return "Ring + Fair Conductor"; } void select() { brightnessBar.setValue(280); setForceBar(3); conductFillRect(0, gridSizeY/2+3, gridSizeX-1, gridSizeY/2+5, .5); } Setup createNext() { return new OscRingEddy2Setup(); } } class OscRingEddy2Setup extends OscRingSetup { String getName() { return "Ring + Poor Conductor"; } void select() { brightnessBar.setValue(280); setForceBar(3); conductFillRect(0, gridSizeY/2+3, gridSizeX-1, gridSizeY/2+5, .1); } Setup createNext() { return new WireEddy1Setup(); } } class WireEddy1Setup extends Setup { String getName() { return "Wire + Fair Conductor"; } void doSetupSources() { setForceBar(3); sourceChooser.select(SRC_1S1F); setSources(); sources[0].y = gridSizeY/2; sources[0].x = gridSizeX/2; } void select() { brightnessBar.setValue(280); conductFillRect(0, gridSizeY/2+3, gridSizeX-1, gridSizeY/2+5, .5); } Setup createNext() { return new WireEddy2Setup(); } } class WireEddy2Setup extends WireEddy1Setup { String getName() { return "Wire + Poor Conductor"; } void select() { brightnessBar.setValue(280); conductFillRect(0, gridSizeY/2+3, gridSizeX-1, gridSizeY/2+5, .1); } Setup createNext() { return new OscRingPermSetup(); } } class OscRingPermSetup extends Setup { String getName() { return "Rings + Ferromagnet"; } void doSetupSources() { sourceChooser.select(SRC_2S1F); setSources(); sources[0].y = sources[1].y = gridSizeY/2; sources[0].x = gridSizeX/2 - 4; sources[1].x = gridSizeX/2 + 4; auxBar.setValue(40); } void select() { setForceBar(6); brightnessBar.setValue(94); addConductor(gridSizeX/2-4, gridSizeY/2+10, .5); addConductor(gridSizeX/2+4, gridSizeY/2+10, .5); addConductor(gridSizeX/2-4, gridSizeY/2-10, .5); addConductor(gridSizeX/2+4, gridSizeY/2-10, .5); permFillRect(gridSizeX/2-2, gridSizeY/2-1, gridSizeX/2+2, windowOffsetY+windowHeight-1, 50); conductFillRect(gridSizeX/2-2, gridSizeY/2-1, gridSizeX/2+2, windowOffsetY+windowHeight-1, .05); } Setup createNext() { return new SolenoidOscSetup(); } } class SolenoidOscSetup extends Setup { String getName() { return "Osc. Solenoid"; } void select() { sourceChooser.select(SRC_2S1F_PLANE); setSources(); int h = windowHeight/3; int cy = gridSizeY/2; sources[0].x = sources[1].x = gridSizeX/2-3; sources[2].x = sources[3].x = gridSizeX/2+3; sources[0].y = sources[2].y = cy-h; sources[1].y = sources[3].y = cy+h; auxBar.setValue(40); setForceBar(9); } void doSetupSources() {} Setup createNext() { return new TransformerSetup(); } } class TransformerSetup extends SolenoidOscSetup { String getName() { return "Transformer"; } void select() { super.select(); int h = windowHeight/3; int cy = gridSizeY/2; conductDrawRect(gridSizeX/2-5, cy-h, gridSizeX/2-5, cy+h, .9); conductDrawRect(gridSizeX/2+5, cy-h, gridSizeX/2+5, cy+h, .9); brightnessBar.setValue(340); } Setup createNext() { return new ToroidalSolenoidOscSetup(); } } class ToroidalSolenoidOscSetup extends Setup { String getName() { return "Osc Toroidal Solenoid"; } void select() { setSources(); sources[0].x = windowOffsetX; sources[0].y = windowOffsetY; sourceChooser.select(SRC_NONE); brightnessBar.setValue(300); setForceBar(8); } void doSetupSources() {} void doStep() { double val = grid[windowOffsetX+gw*windowOffsetY].jz * 30; int i, j; for (i = 0; i != windowWidth; i++) for (j = 0; j != windowHeight; j++) grid[i+windowOffsetX+gw*(j+windowOffsetY)].jz = 0; addWireCircle(gridSizeX/2, gridSizeY/2, windowHeight/3, -val/360., 0, 360); addWireCircle(gridSizeX/2, gridSizeY/2, windowHeight/6, val/360., 0, 360); } Setup createNext() { return new CoaxCableSetup(); } } class CoaxCableSetup extends Setup { String getName() { return "Coaxial Cable"; } void select() { setSources(); sources[0].x = windowOffsetX; sources[0].y = windowOffsetY; brightnessBar.setValue(300); setForceBar(8); } void doSetupSources() {} void doStep() { double val = grid[windowOffsetX+gw*windowOffsetY].jz * 30; int i, j; for (i = 0; i != windowWidth; i++) for (j = 0; j != windowHeight; j++) grid[i+windowOffsetX+gw*(j+windowOffsetY)].jz = 0; int sz = 3; addWireCircle(gridSizeX/2, gridSizeY/2, sz, -val/360., 0, 360); grid[gridSizeX/2+gw*(gridSizeY/2)].jz = val/16; } Setup createNext() { return new CondInOscFieldSetup(); } } class CondInOscFieldSetup extends Setup { String getName() { return "Cond. in Osc. Field"; } void doSetupSources() {} void select() { sourceChooser.select(SRC_2S1F_PLANE); setSources(); int h = windowHeight/3; int cy = gridSizeY/2; sources[0].x = sources[1].x = windowOffsetX; sources[2].x = sources[3].x = windowOffsetX+windowWidth-1; sources[0].y = sources[2].y = 0; sources[1].y = sources[3].y = gridSizeY-1; conductFillRect(gridSizeX/2-4, gridSizeY/2-4, gridSizeX/2+4, gridSizeY/2+4, .4); setForceBar(2); auxBar.setValue(40); } Setup createNext() { return new MovingWireSetup(); } } class MovingWireSetup extends Setup { String getName() { return "Moving Wire"; } double y; int dir, delay; void select() { sourceChooser.select(SRC_NONE); y = windowOffsetY; dir = 1; delay = 0; stopDelay = 200; brightnessBar.setValue(200); } int stopDelay; void doStep() { if (delay > 0) { delay--; filt(); return; } int yi = (int) y; int i; for (i = 0; i != 2; i++) { int gi = gridSizeX/2+i+gw*yi; grid[gi].jz = 0; grid[gi+gw].jz = 0; grid[gi+gw+gw].jz = 0; } y += dir*.06; int yi2 = (int) y; if (yi != yi2) { if (yi2 == gridSizeY/2) delay = stopDelay; if (yi2 == windowOffsetY || yi2 == windowOffsetY+windowHeight-3) { dir = -dir; delay = stopDelay; } } yi = yi2; float yfrac = (float) (y-yi); for (i = 0; i != 2; i++) { int gi = gridSizeX/2+i+gw*yi; grid[gi].jz = (1-yfrac)*.25; grid[gi+gw].jz = .25; grid[gi+gw+gw].jz = yfrac*.25; } filt(); calcBoundaries(); } int filtstep = 0; void filt() { // do filtering around the moving wire int xi = gridSizeX/2; int yi = (int) y; int x, y; int r = 10; for (y = yi-r; y <= yi+r; y++) for (x = xi-r; x <= xi+r; x++) { int gi = x+y*gw; OscElement oe = grid[gi]; if (oe.jz != 0 || oe.conductivity > 0) continue; double rr = Math.sqrt((y-yi)*(y-yi)+ (x-xi)*(x-xi)); double mult1 = 8+rr; double mult2 = 4+mult1; oe.az = (oe.az*mult1 + grid[gi-1].az + grid[gi+1].az + grid[gi-gw].az + grid[gi+gw].az)/mult2; } } Setup createNext() { return new MovingWireTubeSetup(); } } class MovingWireTubeSetup extends MovingWireSetup { String getName() { return "Moving Wire in Tube"; } void select() { super.select(); int w = 4; conductFillRect(gridSizeX/2-w, windowOffsetY, gridSizeX/2-w, windowOffsetY+windowHeight, .6); w++; conductFillRect(gridSizeX/2+w, windowOffsetY, gridSizeX/2+w, windowOffsetY+windowHeight, .6); stopDelay = 500; brightnessBar.setValue(500); } Setup createNext() { return new MovingMagnetSetup(); } } class MovingMagnetSetup extends Setup { String getName() { return "Moving Magnet in Tube"; } double y; int dir, delay; void select() { sourceChooser.select(SRC_NONE); int w = 5; conductFillRect(gridSizeX/2-w, windowOffsetY, gridSizeX/2-w, windowOffsetY+windowHeight, .6); conductFillRect(gridSizeX/2+w, windowOffsetY, gridSizeX/2+w, windowOffsetY+windowHeight, .6); y = windowOffsetY; dir = 1; delay = 0; brightnessBar.setValue(250); } void doStep() { if (delay > 0) { delay--; filt(); return; } int yi = (int) y; int x, i; for (x = -3; x <= 3; x++) { for (i = 0; i <= 2; i++) grid[gridSizeX/2+x+gw*(yi+i)].my = 0; } y += dir*.06; int yi2 = (int) y; if (yi != yi2) { if (yi2 == gridSizeY/2) delay = 500; if (yi2 == windowOffsetY || yi2 == windowOffsetY+windowHeight-3) { dir = -dir; delay = 500; } } yi = yi2; float yfrac = (float) (y-yi); for (x = -3; x <= 3; x++) { int gi = gridSizeX/2 + x + gw*yi; grid[gi].my = -(1-yfrac); grid[gi+gw].my = -1; grid[gi+gw+gw].my = -yfrac; } calcBoundaries(); /*for (x = -4; x <= 4; x++) for (i = -1; i <= 3; i++) grid[gridSizeX/2+x][yi+i].boundary = true;*/ filt(); } int filtstep = 0; void filt() { // run a filter around the moving wire int xi = gridSizeX/2; int yi = (int) y; int x, y; int r = 12; double mult1 = 8; double mult2 = 4+mult1; for (y = yi-r; y <= yi+r; y++) for (x = xi-r; x <= xi+r; x++) { int gi = x+gw*y; OscElement oe = grid[gi]; if (oe.jz != 0 || oe.conductivity > 0) continue; double jz = grid[gi-1].my - grid[gi+1].my + grid[gi+gw].mx - grid[gi-gw].mx; if (jz != 0) continue; oe.az = (oe.az*mult1 + grid[gi-1].az + grid[gi+1].az + grid[gi-gw].az + grid[gi+gw].az)/mult2; } } Setup createNext() { return new RotatingMagnet1Setup(); } } class RotatingMagnet1Setup extends Setup { String getName() { return "Rotating Magnet 1"; } double mt; void select() { sourceChooser.select(SRC_NONE); grid[gridSizeX/2+gw*(gridSizeY/2)].mx = 1; calcBoundaries(); setForceBar(10); mt = 0; brightnessBar.setValue(500); } void doStep() { mt += forceBar.getValue() * .003; grid[gridSizeX/2+gw*(gridSizeY/2)].mx = (float) Math.cos(mt); grid[gridSizeX/2+gw*(gridSizeY/2)].my = (float) -Math.sin(mt); doFilter(); } Setup createNext() { return new RotatingMagnet2Setup(); } } class RotatingMagnet2Setup extends RotatingMagnet1Setup { String getName() { return "Rotating Magnet 2"; } void doStep() { mt += forceBar.getValue() * .003; grid[gridSizeX/2+gw*(gridSizeY/2)].mx = (float) Math.cos(mt); grid[gridSizeX/2+gw*(gridSizeY/2)].my = (float) -Math.abs(Math.sin(mt)); doFilter(); brightnessBar.setValue(500); } Setup createNext() { return new Scattering1Setup(); } } class Scattering1Setup extends Setup { String getName() { return "Scattering 1"; } int ctr; void select() { sourceChooser.select(SRC_1S1F_PLANE); brightnessBar.setValue(100); setForceBar(23); int i, j; for (i = gridSizeX/2-1; i <= gridSizeX/2+1; i++) for (j = gridSizeY/2-1; j <= gridSizeY/2+1; j++) grid[i+gw*j].resonant = true; } void doStep() { ctr++; if (ctr >= 600 && ctr <= 700) { double c = (ctr-600)*.01; sourceMult = 1-c; } else if (ctr >= 1100) { double c = (ctr-1100)*.01; sourceMult = c; if (ctr == 1200) ctr = 0; } } Setup createNext() { return new Scattering2Setup(); } } class Scattering2Setup extends Scattering1Setup { String getName() { return "Scattering 2"; } int ctr; void select() { super.select(); setForceBar(16); } Setup createNext() { return new BigModeSetup(); } } class BigModeSetup extends Setup { String getName() { return "Big TM11 Mode"; } void select() { sourceChooser.select(SRC_NONE); int i; int n = windowWidth*3/4; int x = windowOffsetX+windowWidth/2-n/2; int y = windowOffsetY+windowHeight/2-n/2; for (i = 1; i != 4; i++) conductDrawRect(x-i, y-i, x+n+i-1, y+n+i-1, 1); setupMode(x, y, n, n, 1, 1); brightnessBar.setValue(200); } Setup createNext() { return new OneByOneModesSetup(); } } void setupMode(int x, int y, int sx, int sy, int nx, int ny) { int i, j; for (i = 0; i != sx; i++) for (j = 0; j != sy; j++) { grid[i+x+gw*(j+y)].az = 2* (Math.sin(pi*nx*(i+1)/(sx+1))* Math.sin(pi*ny*(j+1)/(sy+1))); grid[i+x+gw*(j+y)].dazdt = 0; } noFilter(); } class OneByOneModesSetup extends Setup { String getName() { return "TM11 Modes"; } void select() { sourceChooser.select(SRC_NONE); int i, j; int y = 1; int ny = 5; while (y + ny < windowHeight) { int nx = ((y+ny)*(windowWidth-8)/windowHeight)+6; int y1 = y + windowOffsetY; int x1 = windowOffsetX + 1; conductDrawRect(x1-1, y1-1, x1+nx, y1+ny, 1); setupMode(x1, y1, nx, ny, 1, 1); y += ny+2; } } Setup createNext() { return new OneByNModesSetup(); } } class OneByNModesSetup extends Setup { String getName() { return "TMn1 Modes"; } void select() { sourceChooser.select(SRC_NONE); int i, j; int y = 1; int ny = 8; int nx = windowWidth-2; int mode = 1; while (y + ny < windowHeight) { int y1 = y + windowOffsetY; int x1 = windowOffsetX + 1; conductDrawRect(x1-1, y1-1, x1+nx, y1+ny, 1); setupMode(x1, y1, nx, ny, mode, 1); y += ny+2; mode++; } } Setup createNext() { return new NByNModesSetup(); } } class NByNModesSetup extends Setup { String getName() { return "TMnn Modes"; } void select() { sourceChooser.select(SRC_NONE); int i, j; int y = 1; int modex, modey; int maxmode = 3; if (resBar.getValue() >= 70) maxmode++; if (resBar.getValue() >= 100) maxmode++; int ny = windowHeight/maxmode-2; int nx = windowWidth/maxmode-2; for (modex = 1; modex <= maxmode; modex++) for (modey = 1; modey <= maxmode; modey++) { int x1 = windowOffsetX + 1 + (ny+2)*(modey-1); int y1 = windowOffsetY + 1 + (nx+2)*(modex-1); conductDrawRect(x1-1, y1-1, x1+nx, y1+ny, 1); setupMode(x1, y1, nx, ny, modex, modey); } } Setup createNext() { return new OneByNModeCombosSetup(); } } class OneByNModeCombosSetup extends Setup { String getName() { return "TMn1 Mode Combos"; } void select() { sourceChooser.select(SRC_NONE); int i, j; int y = 1; int ny = 8; int nx = windowWidth-2; while (y + ny < windowHeight) { int mode1 = getrand(8)+1; int mode2; do mode2 = getrand(8)+1; while (mode1 == mode2); int y1 = y + windowOffsetY; int x1 = windowOffsetX + 1; conductDrawRect(x1-1, y1-1, x1+nx, y1+ny, 1); for (i = 0; i != nx; i++) for (j = 0; j != ny; j++) { grid[i+x1+gw*(j+y1)].az = (float) 2* (Math.sin(mode1*pi*(i+1)/(nx+1))* Math.sin(pi*(j+1)/(ny+1))*.5 + Math.sin(mode2*pi*(i+1)/(nx+1))* Math.sin(pi*(j+1)/(ny+1))*.5); grid[i+x1+gw*(j+y1)].dazdt = 0; } y += ny+2; } noFilter(); } Setup createNext() { return new NByNModeCombosSetup(); } } class NByNModeCombosSetup extends Setup { String getName() { return "TMnn Mode Combos"; } void select() { sourceChooser.select(SRC_NONE); int i, j; int y = 1; int maxmode = 2; if (resBar.getValue() >= 70) maxmode++; if (resBar.getValue() >= 100) maxmode++; int ny = windowHeight/maxmode-2; int nx = windowWidth/maxmode-2; int gx, gy; for (gx = 1; gx <= maxmode; gx++) for (gy = 1; gy <= maxmode; gy++) { int mode1x = getrand(4)+1; int mode1y = getrand(4)+1; int mode2x, mode2y; do { mode2x = getrand(4)+1; mode2y = getrand(4)+1; } while (mode1x == mode2x && mode1y == mode2y); int x1 = windowOffsetX + 1 + (ny+2)*(gx-1); int y1 = windowOffsetY + 1 + (nx+2)*(gy-1); conductDrawRect(x1-1, y1-1, x1+nx, y1+ny, 1); for (i = 0; i != nx; i++) for (j = 0; j != ny; j++) { grid[i+x1+gw*(j+y1)].az = 2* (Math.sin(mode1x*pi*(i+1)/(nx+1))* Math.sin(mode1y*pi*(j+1)/(ny+1))*.5 + Math.sin(mode2x*pi*(i+1)/(nx+1))* Math.sin(mode2y*pi*(j+1)/(ny+1))*.5); grid[i+x1+gw*(j+y1)].dazdt = 0; } } noFilter(); } Setup createNext() { return new TriangleModesSetup(); } } class TriangleModesSetup extends Setup { String getName() { return "Triangle Modes"; } void select() { sourceChooser.select(SRC_NONE); int i, j; for (i = 0; i != 2; i++) for (j = 0; j != 2; j++) { int x = windowOffsetX+windowWidth*i/2+1; int y = windowOffsetY+windowHeight*j/2+1; int w = windowWidth/2-2; int h = windowHeight/2-2; int k; for (k = 0; k != w; k++) conductDrawRect(x+k+1, y+k, x+w, y+k, 1); conductDrawRect(x-1, y-1, x+w, y+h, 1); int mx = 0, my = 0; switch (j*2+i) { case 0: mx = 1; my = 2; break; case 1: mx = 1; my = 3; break; case 2: mx = 2; my = 3; break; case 3: mx = 1; my = 4; break; } int xi, yi; for (yi = 0; yi != h; yi++) { for (xi = 0; xi <= yi; xi++) grid[x+xi+gw*(y+yi)].az = (Math.sin(mx*pi*(xi+1)/(w+2))* Math.sin(my*pi*(yi+2)/(h+2)) - Math.sin(my*pi*(xi+1)/(w+2))* Math.sin(mx*pi*(yi+2)/(h+2))); grid[x+xi+gw*(y+yi)].dazdt = 0; } } brightnessBar.setValue(114); noFilter(); } Setup createNext() { return new CircleModes1Setup(); } } class CircleModes1Setup extends Setup { String getName() { return "Circular Modes 1"; } void select() { sourceChooser.select(SRC_NONE); int i, j; for (i = 0; i != 2; i++) for (j = 0; j != 2; j++) { int x = windowOffsetX+windowWidth*i/2+1; int y = windowOffsetY+windowHeight*j/2; int w = windowWidth/2-2; int h = windowHeight/2-2; conductFillRect(x-1, y-1, x+w+1, y+h+1, 1); int k, r = w/2; double jj[] = new double[3]; double omega = zeroj(i, j+1)/r; double mult = 1; switch (j*2+i) { case 1: mult = 1/.6; break; case 2: mult = 2; break; case 3: mult = 5/.6; break; } for (k = -r; k <= r; k++) { int yy = (int) Math.sqrt(r*r-k*k-.00001); conductFillRect(x+r+k, y+r-yy, x+r+k, y+r+yy, 0); int l; for (l = -yy; l <= yy; l++) { double rr = Math.sqrt(k*k+l*l); double r0 = rr*omega; double angfunc = (i == 0) ? 1 : l/rr; if (rr == 0) angfunc = (i == 0) ? 1 : 0; bess(i, r0, jj); grid[x+r+k+gw*(y+r+l)].az = jj[i+1]*angfunc*mult; } } } brightnessBar.setValue(200); } Setup createNext() { return new CircleModes2Setup(); } } class CircleModes2Setup extends Setup { String getName() { return "Circular Modes 2"; } void select() { sourceChooser.select(SRC_NONE); int x = windowOffsetX+1; int y = windowOffsetY+1; int w = windowWidth-2; int h = windowHeight-2; conductFillRect(x-1, y-1, x+w+1, y+h+1, 1); int k, r = w/2; double jj[] = new double[3]; double omega = zeroj(1, 1)/r; double tmult = 2*r/16.; for (k = -r; k <= r; k++) { int yy = (int) Math.sqrt(r*r-k*k-.00001); conductFillRect(x+r+k, y+r-yy, x+r+k, y+r+yy, 0); int l; for (l = -yy; l <= yy; l++) { double rr = Math.sqrt(k*k+l*l); double r0 = rr*omega; double angfunc1 = l/rr; double angfunc2 = k/rr; if (rr == 0) angfunc1 = angfunc2 = 0; bess(1, r0, jj); grid[x+r+k+gw*(y+r+l)].az = jj[2]*angfunc1*tmult; grid[x+r+k+gw*(y+r+l)].dazdt = jj[2]*angfunc2; } } brightnessBar.setValue(200); } Setup createNext() { return new Waveguides1Setup(); } } double zeroj( int m_order, int n_zero) { // Zeros of the Bessel function J(x) // Inputs // m_order Order of the Bessel function // n_zero Index of the zero (first, second, etc.) // Output // z The "n_zero"th zero of the Bessel function //* Use asymtotic formula for initial guess double beta = (n_zero + 0.5*m_order - 0.25)*(3.141592654); double mu = 4*m_order*m_order; double beta8 = 8*beta; double z = beta - (mu-1)/beta8 - 4*(mu-1)*(7*mu-31)/(3*beta8*beta8*beta8); //* Use Newton's method to locate the root double jj[] = new double[m_order+3]; int i; double deriv; for( i=1; i<=5; i++ ) { bess( m_order+1, z, jj ); // Remember j(1) is J_0(z) // Use the recursion relation to evaluate derivative deriv = -jj[m_order+2] + m_order/z * jj[m_order+1]; z -= jj[m_order+1]/deriv; // Newton's root finding } return(z); } void bess( int m_max, double x, double jj[] ) { // Bessel function // Inputs // m_max Largest desired order // x = Value at which Bessel function J(x) is evaluated // Output // jj = Vector of J(x) for order m = 0, 1, ..., m_max //* Perform downward recursion from initial guess int maxmx = (m_max > x) ? m_max : ((int)x); // Max(m,x) // Recursion is downward from m_top (which is even) int m_top = 2*((int)( (maxmx+15)/2 + 1 )); double j[] = new double[m_top+2]; j[m_top+1] = 0.0; j[m_top] = 1.0; double tinyNumber = 1e-16; int m; for( m=m_top-2; m>=0; m--) // Downward recursion j[m+1] = 2*(m+1)/(x+tinyNumber)*j[m+2] - j[m+3]; //* Normalize using identity and return requested values double norm = j[1]; // NOTE: Be careful, m=0,1,... but for( m=2; m<=m_top; m+=2 ) // vector goes j(1),j(2),... norm += 2*j[m+1]; for( m=0; m<=m_max; m++ ) // Send back only the values for jj[m+1] = j[m+1]/norm; // m=0,...,m_max and discard values } // for m=m_max+1,...,m_top class Waveguides1Setup extends Setup { String getName() { return "Waveguides 1"; } void select() { sourceChooser.select(SRC_1S1F_PLANE); int i, j; int x = 1; int nx = 5; int y1 = windowOffsetY + 2; while (x + nx < windowWidth) { int x1 = x + windowOffsetX; conductDrawRect(x1-1, y1-1, x1-1, gridSizeY-1, 1); conductDrawRect(x1+nx, y1-1, x1+nx, gridSizeY-1, 1); nx += 2; x += nx; } conductDrawRect(x-1+windowOffsetX, y1-1, gridSizeX-1, y1-1, 1); brightnessBar.setValue(140); setForceBar(28); } Setup createNext() { return new Waveguides2Setup(); } } class Waveguides2Setup extends Waveguides1Setup { String getName() { return "Waveguides 2"; } void select() { super.select(); setForceBar(17); } Setup createNext() { return new Waveguides3Setup(); } } class Waveguides3Setup extends Setup { String getName() { return "Waveguides 3"; } void select() { sourceChooser.select(SRC_1S1F_PLANE); int i, j; int x = 1; int nx = 8; int y1 = windowOffsetY + 2; conductDrawRect(windowOffsetX+1, y1-1, windowOffsetX+windowWidth-1, y1-1, 1); x = 1; j = 0; while (x + nx < windowWidth && j < nx) { int x1 = x + windowOffsetX; conductDrawRect(x1-1, y1-1, x1-1, gridSizeY-1, 1); conductDrawRect(x1+nx, y1-1, x1+nx, gridSizeY-1, 1); addConductor(x1+j++, y1-1, 0); x += nx+2; if (resBar.getValue() == 32 && j == 2) j++; } brightnessBar.setValue(1000); setForceBar(32); } Setup createNext() { return new Waveguides4Setup(); } } class Waveguides4Setup extends Waveguides3Setup { String getName() { return "Waveguides 4"; } void select() { sourceChooser.select(SRC_1S1F_PLANE); int i; int x = 1; int nx = 9; int y1 = windowOffsetY + 2; int ny = windowHeight-1; x = 1; while (x + nx < windowWidth) { int x1 = x + windowOffsetX; conductDrawRect(x1-1, y1-1, x1-1, y1+ny-2, 1); conductDrawRect(x1+nx, y1-1, x1+nx, y1+ny-2, 1); x += nx+2; } brightnessBar.setValue(480); setForceBar(40); } void doStep() { int y = windowOffsetY; int nx = 9; int x = 1; int g = 1; while (x + nx < windowWidth) { int x1 = x + windowOffsetX; int j; int n1 = 1; int n2 = 1; switch (g) { case 1: n1 = n2 = 1; break; case 2: n1 = n2 = 2; break; case 3: n1 = n2 = 3; break; case 4: n1 = 1; n2 = 2; break; case 5: n1 = 1; n2 = 3; break; case 6: n1 = 2; n2 = 3; break; default: n1 = n2 = 0; break; } for (j = 0; j != nx; j++) { grid[x1+j+gw*y].az = grid[x1+j+gw*y].jz * (Math.sin(pi*n1*(j+1)/(nx+1)) + Math.sin(pi*n2*(j+1)/(nx+1))); grid[x1+j+gw*y].jz = 0; } x += nx+2; g++; } } Setup createNext() { return new ResonantCavitiesSetup(); } } class ResonantCavitiesSetup extends Setup { String getName() { return "Resonant Cavities"; } void select() { sourceChooser.select(SRC_1S1F_PLANE); int i, j; int x = 1; int nx = 5; int y1 = windowOffsetY + 11; while (x + nx < windowWidth) { int ny = ((x+nx)*(windowHeight-18)/windowWidth)+6; int x1 = x + windowOffsetX; for (i = 0; i != ny+2; i++) { addConductor(x1- 1, y1+i-1, 1); addConductor(x1+nx, y1+i-1, 1); } for (j = 0; j != nx+2; j++) { addConductor(x1+j-1, y1-1, 1); addConductor(x1+j-1, y1+ny, 1); } addConductor(x1+nx/2, y1-1, 0); x += nx+2; } x--; for (; x < windowWidth; x++) addConductor(x+windowOffsetX, y1-1, 1); brightnessBar.setValue(300); setForceBar(38); } Setup createNext() { return new SingleSlitSetup(); } } class SingleSlitSetup extends Setup { String getName() { return "Single Slit"; } void select() { sourceChooser.select(SRC_1S1F_PLANE); int x = gridSizeX/2; int y = windowOffsetY+4; conductFillRect(0, y, gridSizeX-1, y+2, 1); conductFillRect(x-7, y, x+7, y+2, 0); brightnessBar.setValue(275); setForceBar(35); } Setup createNext() { return new DoubleSlitSetup(); } } class DoubleSlitSetup extends Setup { String getName() { return "Double Slit"; } void select() { sourceChooser.select(SRC_1S1F_PLANE); int x = gridSizeX/2; int y = windowOffsetY+4; conductFillRect(0, y, gridSizeX-1, y+2, 1); conductFillRect(x-7, y, x-5, y+2, 0); conductFillRect(x+5, y, x+7, y+2, 0); brightnessBar.setValue(366); setForceBar(35); } Setup createNext() { return new TripleSlitSetup(); } } class TripleSlitSetup extends Setup { String getName() { return "Triple Slit"; } void select() { sourceChooser.select(SRC_1S1F_PLANE); int x = gridSizeX/2; int y = windowOffsetY+4; conductFillRect(0, y, gridSizeX-1, y+2, 1); conductFillRect(x-13, y, x-11, y+2, 0); conductFillRect(x -1, y, x +1, y+2, 0); conductFillRect(x+11, y, x+13, y+2, 0); brightnessBar.setValue(310); setForceBar(35); } Setup createNext() { return new ObstacleSetup(); } } class ObstacleSetup extends Setup { String getName() { return "Obstacle"; } void select() { sourceChooser.select(SRC_1S1F_PLANE); int x = gridSizeX/2; int y = windowOffsetY+6; conductFillRect(x-7, y, x+7, y+2, 1); brightnessBar.setValue(400); setForceBar(35); } Setup createNext() { return new HalfPlaneSetup(); } } class HalfPlaneSetup extends Setup { String getName() { return "Half Plane"; } void select() { sourceChooser.select(SRC_1S1F_PLANE); int x = windowOffsetX+windowWidth/2; int i; conductFillRect(windowOffsetX+windowWidth*2/3, windowOffsetY+3, windowOffsetY+windowWidth-1, windowOffsetY+5, 1); brightnessBar.setValue(150); setForceBar(35); } Setup createNext() { return new LloydsMirrorSetup(); } } class LloydsMirrorSetup extends Setup { String getName() { return "Lloyd's Mirror"; } void select() { setSources(); sources[0].x = windowOffsetX; sources[0].y = windowOffsetY + windowHeight*3/4; brightnessBar.setValue(120); setForceBar(40); conductDrawRect(0, windowOffsetY+windowHeight-1, gridSizeX-1, windowOffsetY+windowHeight-1, 1); } void doSetupSources() {} Setup createNext() { return null; } } void addThickWire(int cx, int cy, int r, double j) { int res = 4; cx *= res; cy *= res; r *= res; j /= (res*res); int x, y; for (x = -r; x <= r; x++) { int yd = (int) Math.sqrt(r*r-x*x); for (y = -yd; y <= yd; y++) grid[( x+cx)/res+gw*((y+cy)/res)].jz += j; } } void addWireCircle(int cx, int cy, int r, double j, int deg1, int deg2) { int res = 4; r *= res; j /= (res*res); int th; for (th = deg1; th != deg2; th++) { int x = cx+(int) (Math.cos(th*pi/180)*r/res); int y = cy-(int) (Math.sin(th*pi/180)*r/res); grid[x+gw*y].jz += j; } } void addConductor(int x, int y, double cv) { OscElement oe = grid[x+gw*y]; oe.conductivity = (float) cv; if (cv == 1) oe.az = oe.dazdt = 0; } void addPerm(int x, int y, double pm) { OscElement oe = grid[x+gw*y]; oe.perm = (float) pm; oe.conductivity = (pm == 1) ? 0 : .5f; } void conductFillRect(int x, int y, int x2, int y2, double cv) { int i, j; for (i = x; i <= x2; i++) for (j = y; j <= y2; j++) addConductor(i, j, (float) cv); } void conductDrawRect(int x, int y, int x2, int y2, double cvd) { int i; float cv = (float) cvd; for (i = x; i <= x2; i++) { addConductor(i, y, cv); addConductor(i, y2, cv); } for (i = y; i <= y2; i++) { addConductor(x, i, cv); addConductor(x2, i, cv); } } void permDrawRect(int x, int y, int x2, int y2, double pm) { int i; for (i = x; i <= x2; i++) { addPerm(i, y, pm); addPerm(i, y2, pm); } for (i = y; i <= y2; i++) { addPerm(x, i, pm); addPerm(x2, i, pm); } } void permFillRect(int x, int y, int x2, int y2, double pm) { int i, j; for (i = x; i <= x2; i++) for (j = y; j <= y2; j++) addPerm(i, j, pm); } }