// VecDemo.java (C) 2001 by Paul Falstad, www.falstad.com import java.io.InputStream; import java.awt.*; import java.awt.image.ImageProducer; import java.applet.Applet; import java.applet.AudioClip; import java.util.Vector; import java.util.Hashtable; import java.util.Enumeration; import java.io.File; import java.net.URL; import java.util.Random; import java.awt.image.MemoryImageSource; import java.lang.Math; import java.awt.event.*; class VecDemoCanvas extends Canvas { VecDemo pg; VecDemoCanvas(VecDemo p) { pg = p; } public Dimension getPreferredSize() { return new Dimension(300,400); } public void update(Graphics g) { pg.updateVecDemo(g); } public void paint(Graphics g) { pg.updateVecDemo(g); } }; class VecDemoLayout implements LayoutManager { public VecDemoLayout() {} public void addLayoutComponent(String name, Component c) {} public void removeLayoutComponent(Component c) {} public Dimension preferredLayoutSize(Container target) { return new Dimension(500, 500); } public Dimension minimumLayoutSize(Container target) { return new Dimension(100,100); } public void layoutContainer(Container target) { int cw = target.size().width * 2/3; target.getComponent(0).move(0, 0); target.getComponent(0).resize(cw, target.size().height); int i; int h = 0; 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 = target.size().width - cw; int c = 0; if (false && m instanceof Label) { h += d.height/3; c = (target.size().width-cw-d.width)/2; } m.move(cw+c, h); m.resize(d.width, d.height); h += d.height; } } } }; class Particle { public double x, y; public double vx, vy; public double ang, goalheight; public int lifetime; public Color color; Particle() { vx = vy = ang = 0; } }; class GridElement { public double height, div, curl, normdot, vecX, vecY; }; public class VecDemo extends Applet implements ComponentListener, ActionListener, AdjustmentListener, MouseMotionListener, MouseListener, ItemListener { Thread engine = null; Dimension winSize; Image dbimage; Image backimage; Random random; public String getAppletInfo() { return "VecDemo Series by Paul Falstad"; } static final double pi = 3.14159265358979323846; int getrand(int x) { int q = random.nextInt(); if (q < 0) q = -q; return q % x; } VecDemoCanvas cv; Button resetButton; Button kickButton; Button reverseButton; Button infoButton; Choice functionChooser; Choice motionChooser; Choice floorColorChooser; Scrollbar partCountBar; Checkbox flatCheck; boolean isFlat; double reverse; GridElement grid[][]; int xpoints[]; int ypoints[]; Particle particles[]; int density[][]; int minHeightBuf[][]; static final int gridsize = 80; static final int densitygroupsize = 5; static final int densitygridsize = 16; static final int particleCount = 500; double topz; boolean functionChanged; boolean backgroundChanged; double minpotential; Vector functionList; VecFunction curfunc; static final int MOT_VELOCITY = 0; static final int MOT_FORCE = 1; static final int MOT_CURLERS = 2; static final int MOT_EQUIPOTENTIAL = 3; static final int FC_FIELD = 0; static final int FC_POTENTIAL = 1; static final int FC_CURL = 2; static final int FC_DIV = 3; static final int FC_NONE = 4; int pause = 20; public void init() { try { String param = getParameter("PAUSE"); if (param != null) pause = Integer.parseInt(param); } catch (Exception e) { } Color particleColors[] = new Color[27]; int i; for (i = 0; i != 27; i++) particleColors[i] = new Color(((i%3)+1)*85, ((i/3)%3+1)*85, ((i/9)%3+1)*85); functionList = new Vector(); VecFunction vf = new InverseSquaredRadial(); while (vf != null) { functionList.addElement(vf); vf = vf.createNext(); } density = new int[densitygridsize][densitygridsize]; random = new Random(); particles = new Particle[particleCount]; for (i = 0; i != particleCount; i++) { particles[i] = new Particle(); particles[i].color = particleColors[i % 27]; } xpoints = new int[4]; ypoints = new int[4]; setLayout(new VecDemoLayout()); cv = new VecDemoCanvas(this); cv.addComponentListener(this); //cv.addMouseMotionListener(this); //cv.addMouseListener(this); add(cv); resetButton = new Button("Reset"); add(resetButton); resetButton.addActionListener(this); kickButton = new Button("Kick"); add(kickButton); kickButton.addActionListener(this); kickButton.disable(); reverseButton = new Button("Reverse"); add(reverseButton); reverseButton.addActionListener(this); infoButton = new Button("Function Info"); add(infoButton); infoButton.addActionListener(this); add(new Label("Field selection:")); functionChooser = new Choice(); for (i = 0; i != functionList.size(); i++) functionChooser.add( ((VecFunction) functionList.elementAt(i)).getName()); add(functionChooser); curfunc = (VecFunction) functionList.elementAt(0); functionChooser.addItemListener(this); add(new Label("Particle movement:")); motionChooser = new Choice(); motionChooser.add("Velocity"); motionChooser.add("Force"); motionChooser.add("Curl Detectors"); motionChooser.addItemListener(this); add(motionChooser); add(new Label("Floor colors:")); floorColorChooser = new Choice(); floorColorChooser.add("field magnitude"); floorColorChooser.add("potential"); floorColorChooser.add("curl z"); floorColorChooser.add("divergence"); floorColorChooser.add("none"); floorColorChooser.addItemListener(this); add(floorColorChooser); flatCheck = new Checkbox("Flat View"); flatCheck.addItemListener(this); add(flatCheck); add(new Label("Number of Particles", Label.CENTER)); add(partCountBar = new Scrollbar(Scrollbar.HORIZONTAL, 500, 1, 1, 500)); add(new Label("", Label.CENTER)); add(new Label("by Paul Falstad", Label.CENTER)); add(new Label("http://www.falstad.com", Label.CENTER)); cv.setBackground(Color.black); cv.setForeground(Color.white); reverse = 1; functionChanged = backgroundChanged = true; reinit(); repaint(); } void generateFunction() { int x, y; minpotential = 10000; if (grid == null) grid = new GridElement[gridsize][gridsize]; curfunc = (VecFunction) functionList.elementAt(functionChooser.getSelectedIndex()); double mu, xx, xx2, yy, yy2, r, r1, r2, r3, r4; curfunc.reverse = reverse; double levelheight = curfunc.getLevelHeight(); for (x = 0; x != gridsize; x++) for (y = 0; y != gridsize; y++) { GridElement ge = grid[x][y] = new GridElement(); ge.curl = ge.div = ge.height = 0; curfunc.setGrid(ge, x, y); ge.curl *= reverse; ge.div *= reverse; ge.height = ge.height*reverse+levelheight; ge.vecX *= reverse; ge.vecY *= reverse; if (ge.height < minpotential) minpotential = ge.height; if (ge.vecX == 0 && ge.vecY == 0) ge.vecX = ge.vecY = .0001; // light vector = (1,1,1)/1.73 // norm vector = (-dzdx, -dzdy, 1) ge.normdot = (ge.vecX+ge.vecY+1)*(1/1.73)/ java.lang.Math.sqrt(ge.vecX*ge.vecX+ge.vecY*ge.vecY+1); //double lightx = -20-(x*20.0/gridsize-10); //double lighty = -20-(y*20.0/gridsize-10); //double lightz = 5-ge.height; //ge.normdot = (ge.vecX*lightx+ge.vecY*lighty+lightz) / // java.lang.Math.sqrt((ge.vecX*ge.vecX+ge.vecY*ge.vecY+1)* // (lightx*lightx+lighty*lighty+lightz*lightz)); } topz = grid[0][0].height; topz = curfunc.fixTopZ(topz); minpotential += .01; if (curfunc.nonGradient()) { minpotential = -10000; if (motionChooser.countItems() > 3) motionChooser.remove("Equipotential"); } else if (motionChooser.countItems() <= 3) motionChooser.add("Equipotential"); } Color computeColor(GridElement ge, double c) { if (c < 0) c = 0; if (c > 1) c = 1; //System.out.print(c + "\n"); c = .5 + c * .5; double value = 0; double range = 10; double offset = 4; switch (floorColorChooser.getSelectedIndex()) { case FC_FIELD: value = ge.vecX*ge.vecX+ge.vecY*ge.vecY; offset = 7; range = 15; break; case FC_POTENTIAL: value = ge.height-curfunc.getLevelHeight(); offset = 1; range = 4; break; case FC_CURL: value = ge.curl; offset = 4; range = 10; break; case FC_DIV: value = ge.div; offset = 3; range = 11; break; case FC_NONE: break; } double redness = (value < 0) ? (java.lang.Math.log(-value)+offset)/range : 0; double grnness = (value > 0) ? (java.lang.Math.log( value)+offset)/range : 0; if (redness > 1) redness = 1; if (grnness > 1) grnness = 1; if (grnness < 0) grnness = 0; if (redness < 0) redness = 0; double grayness = (1-(redness+grnness))*c; double gray = .6; return new Color((int) ((c*redness+gray*grayness)*255), (int) ((c*grnness+gray*grayness)*255), (int) ((gray*grayness)*255)); } int addToDensityGroup(Particle p) { int a = ((int)p.x)/densitygroupsize; int b = ((int)p.y)/densitygroupsize; int n = ++density[a][b]; if (n > particleCount) System.out.print(a + " " + b + " " + density[a][b] + "\n"); return n; } void removeFromDensityGroup(Particle p) { int a = ((int)p.x)/densitygroupsize; int b = ((int)p.y)/densitygroupsize; if (--density[a][b] < 0) { System.out.print(a + " " + b + " " + density[a][b] + "\n"); } } void positionParticle(Particle p) { int x, y; int bestx = 0, besty = 0; int best = 10000; // we avoid scanning the grid in the same order every time // so that we treat equal-density squares as equally as possible. int randaddx = getrand(densitygridsize); int randaddy = getrand(densitygridsize); for (x = 0; x != densitygridsize; x++) for (y = 0; y != densitygridsize; y++) { int ix = (randaddx + x) % densitygridsize; int iy = (randaddy + y) % densitygridsize; if (density[ix][iy] <= best) { bestx = ix; besty = iy; best = density[ix][iy]; } } p.x = bestx*densitygroupsize + getrand(densitygroupsize*10)/10.0; p.y = besty*densitygroupsize + getrand(densitygroupsize*10)/10.0; p.goalheight = getHeight(p.x, p.y); p.lifetime = 500; p.vx = (getrand(10)/9.0-.5)*.2; p.vy = (getrand(10)/9.0-.5)*.2; p.ang = (getrand(100)*(3.145926535*2/100)); } int getParticleCount() { return partCountBar.getValue(); } void resetParticles() { int i, j; for (i = 0; i != densitygridsize; i++) for (j = 0; j != densitygridsize; j++) density[i][j] = 0; for (i = 0; i != particleCount; i++) { Particle p = particles[i]; p.x = getrand(gridsize*10)/10.0; p.y = getrand(gridsize*10)/10.0; p.goalheight = getHeight(p.x, p.y); p.lifetime = i*2; p.vx = (getrand(10)/9.0-.5)*.2; p.vy = (getrand(10)/9.0-.5)*.2; p.ang = (getrand(100)*(3.145926535*2/100)); addToDensityGroup(p); } } void kickParticles() { int i; for (i = 0; i != particleCount; i++) { Particle p = particles[i]; p.vx += (getrand(10)/9.0-.5)*.4; p.vy += (getrand(10)/9.0-.5)*.4; } } void reinit() { Dimension d = winSize = cv.getSize(); if (winSize.width == 0) return; dbimage = createImage(d.width, d.height); backimage = createImage(d.width, d.height); } void drawBackground() { Graphics g = backimage.getGraphics(); isFlat = flatCheck.getState() || curfunc.nonGradient() || motionChooser.getSelectedIndex() == MOT_CURLERS; if (isFlat) { int x, y; for (y = 0; y < gridsize; y++) for (x = 0; x < gridsize; x++) { GridElement ge = grid[x][y]; int nx = x*winSize.width/gridsize; int ny = winSize.height-(y+1)*winSize.height/gridsize; int nx1 = (x+1)*winSize.width/gridsize; int ny1 = winSize.height-y*winSize.height/gridsize; g.setColor(computeColor(ge, .5)); g.fillRect(nx, ny, nx1-nx, ny1-ny); if ((x % 5) == 0) { g.setColor(Color.white); g.drawLine(nx, ny, nx, ny1); } if ((y % 5) == 0) { g.setColor(Color.white); g.drawLine(nx, ny, nx1, ny); } } functionChanged = backgroundChanged = false; return; } scaleworld(); g.setColor(cv.getBackground()); g.fillRect(0, 0, winSize.width, winSize.height); int x, y; minHeightBuf = new int[winSize.width][gridsize]; for (x = 0; x != winSize.width; x++) minHeightBuf[x][0] = winSize.height-1; for (y = 1; y < gridsize; y++) { for (x = gridsize-2; x >= 0; x--) { double nx = x*(20.0/gridsize)-10; double ny = y*(20.0/gridsize)-10; double nx1 = (x+1)*(20.0/gridsize)-10; map3d(nx, ny, grid[x] [y].height, xpoints, ypoints, 0); map3d(nx1, ny, grid[x+1][y].height, xpoints, ypoints, 1); int ix; int ydiff = ypoints[1]-ypoints[0]; int xdiff = xpoints[1]-xpoints[0]; for (ix = xpoints[0]; ix <= xpoints[1]; ix++) { if (ix < 0 || ix >= winSize.width) continue; int iy = (ix-xpoints[0])*ydiff/xdiff+ypoints[0]; minHeightBuf[ix][y] = minHeightBuf[ix][y-1]; if (iy < minHeightBuf[ix][y]) minHeightBuf[ix][y] = iy; } } } for (y = gridsize-2; y >= 0; y--) { for (x = gridsize-2; x >= 0; x--) { double nx = x*(20.0/gridsize)-10; double ny = y*(20.0/gridsize)-10; double nx1 = (x+1)*(20.0/gridsize)-10; double ny1 = (y+1)*(20.0/gridsize)-10; map3d(nx, ny, grid[x] [y].height, xpoints, ypoints, 0); map3d(nx1, ny, grid[x+1][y].height, xpoints, ypoints, 1); map3d(nx, ny1, grid[x] [y+1].height, xpoints, ypoints, 3); map3d(nx1, ny1, grid[x+1][y+1].height, xpoints, ypoints, 2); if (xpoints[0] >= 0 && xpoints[0] < winSize.width && xpoints[2] >= 0 && xpoints[2] < winSize.width && ypoints[0] > minHeightBuf[xpoints[0]][y] && ypoints[2] > minHeightBuf[xpoints[2]][y]) continue; if (ypoints[0] < 0 && ypoints[1] < 0 && ypoints[2] < 0 && ypoints[3] < 0) continue; int wh = winSize.height; if (ypoints[0] > wh && ypoints[1] > wh && ypoints[2] > wh && ypoints[3] > wh) continue; if (ypoints[0] < -50) ypoints[0] = -50; if (ypoints[1] < -50) ypoints[1] = -50; if (ypoints[2] < -50) ypoints[2] = -50; if (ypoints[3] < -50) ypoints[3] = -50; if (ypoints[0] > wh) ypoints[0] = wh; if (ypoints[1] > wh) ypoints[1] = wh; if (ypoints[2] > wh) ypoints[2] = wh; if (ypoints[3] > wh) ypoints[3] = wh; GridElement ge = grid[x][y]; g.setColor(computeColor(ge, ge.normdot)); g.fillPolygon(xpoints, ypoints, 4); g.setColor(Color.white); if ((x % 5) == 0) g.drawLine(xpoints[1], ypoints[1], xpoints[2], ypoints[2]); if ((y % 5) == 0) g.drawLine(xpoints[3], ypoints[3], xpoints[2], ypoints[2]); } } functionChanged = backgroundChanged = false; } 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(); } static final double root2 = 1.4142135623730950488016887242096981; double scalex, scaley; int topy; void map3d(double x, double y, double z, int xpoints[], int ypoints[], int pt) { double realx = x; // range: [-10,10] double realy = z; double realz = y + 20; xpoints[pt] = (winSize.width/2) + (int) (scalex*realx/realz); ypoints[pt] = topy-(int) (scaley*realy/realz); } void scaleworld() { scalex = winSize.width; scaley = -15 * winSize.height / topz; topy = (int) (scaley * topz / 30); } double getHeight(double x, double y) { int ix = (int) x; int iy = (int) y; if (ix >= gridsize-1 || iy >= gridsize-1) return grid[ix][iy].height; double fracx = x-ix; double fracy = y-iy; return grid[ix][iy].height * (1-fracx) * (1-fracy) + grid[ix+1][iy].height * fracx * (1-fracy) + grid[ix][iy+1].height * (1-fracx) * fracy + grid[ix+1][iy+1].height * fracx * fracy; } public void updateVecDemo(Graphics realg) { Graphics g = dbimage.getGraphics(); if (winSize == null || winSize.width == 0) return; if (functionChanged || backgroundChanged) { realg.setColor(cv.getBackground()); FontMetrics fm = realg.getFontMetrics(); String s = "Please wait..."; int w = fm.stringWidth(s); int h = fm.getHeight(); int margin = 20; realg.fillRect((winSize.width-w-margin)/2, (winSize.height-h-margin)/2, w+margin,h+margin); realg.setColor(cv.getForeground()); realg.drawString(s, (winSize.width-w)/2, (winSize.height-h)/2+fm.getAscent()); if (functionChanged) { generateFunction(); resetParticles(); } drawBackground(); } if (xpoints == null) return; g.setColor(cv.getForeground()); g.drawImage(backimage, 0, 0, this); int i; int mot = motionChooser.getSelectedIndex(); boolean withforce = (mot == MOT_FORCE); int bestd = 0; int pcount = getParticleCount(); for (i = 0; i != pcount; i++) { Particle pt = particles[i]; GridElement ge = grid[(int) pt.x][(int) pt.y]; double h = getHeight(pt.x, pt.y); if (mot == MOT_CURLERS) { if (i > (pcount+4)/5) break; g.setColor(pt.color); double ax = java.lang.Math.cos(pt.ang); double ay = java.lang.Math.sin(pt.ang); double offx = ax; double offy = ay; double a1 = curlcalc(pt.x+offx, pt.y+offy, -ay, ax); double a2 = curlcalc(pt.x-offy, pt.y+offx, -ax, -ay); double a3 = curlcalc(pt.x-offx, pt.y-offy, ay, -ax); double a4 = curlcalc(pt.x+offy, pt.y-offx, ax, ay); pt.ang += (a1+a2+a3+a4)/4; int nx1, ny1, nx2, ny2; nx1 = (int) ((pt.x-offx)*winSize.width)/gridsize; ny1 = winSize.height-(int) ((pt.y-offy)*winSize.height)/gridsize; nx2 = (int) ((pt.x+offx)*winSize.width)/gridsize; ny2 = winSize.height-(int) ((pt.y+offy)*winSize.height)/gridsize; g.drawLine(nx1, ny1, nx2, ny2); nx1 = (int) ((pt.x-offy)*winSize.width)/gridsize; ny1 = winSize.height-(int) ((pt.y+offx)*winSize.height)/gridsize; nx2 = (int) ((pt.x+offy)*winSize.width)/gridsize; ny2 = winSize.height-(int) ((pt.y-offx)*winSize.height)/gridsize; g.drawLine(nx1, ny1, nx2, ny2); g.fillOval(nx1-1, ny1-1, 3, 3); } else if (isFlat) { int nx = (int) (pt.x*winSize.width)/gridsize; int ny = winSize.height-(int) (pt.y*winSize.height)/gridsize; g.fillRect(nx, ny, 2, 2); } else { double nx = pt.x*(20.0/gridsize)-10; double ny = pt.y*(20.0/gridsize)-10; map3d(nx, ny, h, xpoints, ypoints, 0); if (xpoints[0] >= 0 && xpoints[0] < winSize.width && ypoints[0] <= minHeightBuf[xpoints[0]][(int) pt.y]+1) g.fillRect(xpoints[0], ypoints[0]-1, 2, 2); } removeFromDensityGroup(pt); if (mot == MOT_EQUIPOTENTIAL) doEquipotential(pt); else curfunc.moveParticle(pt, withforce); if (pt.x < 0 || pt.x >= gridsize || pt.y < 0 || pt.y >= gridsize) pt.x = pt.y = -1; else { int d = addToDensityGroup(pt); if (d > bestd) bestd = d; } if (pt.x >= gridsize || pt.x < 0 || pt.y >= gridsize || pt.y < 0 || getHeight(pt.x, pt.y) > h+1 || pt.lifetime-- < 0) { if (pt.x != -1) removeFromDensityGroup(pt); positionParticle(pt); addToDensityGroup(pt); } else { if (withforce) { pt.vx += ge.vecX*.1; pt.vy += ge.vecY*.1; pt.vx *= .995; pt.vy *= .995; } else { pt.vx = ge.vecX; pt.vy = ge.vecY; } } } if (mot == MOT_VELOCITY && bestd > (6*particleCount/(densitygridsize*densitygridsize))) redistribute(bestd); realg.drawImage(dbimage, 0, 0, this); cv.repaint(pause); } int rediscount; void redistribute(int mostd) { rediscount++; int maxd = (6*particleCount/(densitygridsize*densitygridsize)); int i; for (i = rediscount % 4; i < particleCount; i+=4) { Particle p = particles[i]; int a = ((int)p.x)/densitygroupsize; int b = ((int)p.y)/densitygroupsize; if (density[a][b] <= maxd) continue; p.lifetime = -1; } } double curlcalc(double x, double y, double ax, double ay) { if (x < 0) x = 0; if (y < 0) y = 0; if (x >= gridsize) x = gridsize-1; if (y >= gridsize) y = gridsize-1; int ix = (int) x; int iy = (int) y; return grid[ix][iy].vecX * ax + grid[ix][iy].vecY * ay; } void doEquipotential(Particle p) { double step = (3.14159265/180); double fstep = .9; double a1 = p.ang-step*30; double a2 = p.ang+step*30; double a1h = equipSearch(p, a1); double a2h = equipSearch(p, a2); if (p.goalheight == 0) p.goalheight = getHeight(p.x, p.y); double goal = p.goalheight; double epsilon = .001; double a3 = 0; boolean passone = true; while (true) { if (a2-a1 < epsilon) { if (!passone) { p.x = p.y = -1; return; } a1 = p.ang-step*100; a2 = p.ang+step*100; passone = false; } a3 = (a1+a2)/2; double h = equipSearch(p, a3); if (h >= goal-epsilon && h <= goal+epsilon) break; if ((goal < h && a1h < h) || (goal > h && a1h > h)) a2 = a3; else a1 = a3; } p.ang = a3; p.x += fstep * java.lang.Math.cos(a3); p.y += fstep * java.lang.Math.sin(a3); } double equipSearch(Particle p, double a) { double fstep = .9; double x = p.x + fstep * java.lang.Math.cos(a); double y = p.y + fstep * java.lang.Math.sin(a); if (x < 0 || x >= gridsize || y < 0 || y >= gridsize) return 0; return getHeight(x, y); } public void componentHidden(ComponentEvent e){} public void componentMoved(ComponentEvent e){} public void componentShown(ComponentEvent e) { cv.repaint(pause); } public void componentResized(ComponentEvent e) { backgroundChanged = true; reinit(); cv.repaint(pause); } public void actionPerformed(ActionEvent e) { if (e.getSource() == resetButton) resetParticles(); if (e.getSource() == kickButton) kickParticles(); if (e.getSource() == reverseButton) { reverse *= -1; functionChanged = true; } if (e.getSource() == infoButton) { String s = curfunc.getClass().getName(); try { s = s.substring(s.lastIndexOf('.')+1); getAppletContext().showDocument( new URL(getCodeBase(), "functions.html" + '#' + s), "functionHelp"); } catch (Exception ex) { } } } public void adjustmentValueChanged(AdjustmentEvent e) { } public void mouseDragged(MouseEvent e) { } public void mouseMoved(MouseEvent e) { } public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void itemStateChanged(ItemEvent e) { if (e.getItemSelectable() == flatCheck) { backgroundChanged = true; cv.repaint(pause); return; } if (e.getItemSelectable() == motionChooser) { if (motionChooser.getSelectedIndex() == MOT_FORCE) kickButton.enable(); else kickButton.disable(); if (motionChooser.getSelectedIndex() == MOT_EQUIPOTENTIAL) resetParticles(); if (motionChooser.getSelectedIndex() == MOT_CURLERS) { backgroundChanged = true; cv.repaint(pause); } } if (e.getStateChange() != ItemEvent.SELECTED) return; if (e.getItemSelectable() == floorColorChooser) { backgroundChanged = true; cv.repaint(pause); return; } if (e.getItemSelectable() == functionChooser) { reverse = 1; functionChanged = true; cv.repaint(pause); } } } abstract class VecFunction { abstract String getName(); abstract void setGrid(GridElement ge, int x, int y); static final int gridsize = 80; abstract VecFunction createNext(); boolean nonGradient() { return false; } void moveParticle(Particle p, boolean withforce) { p.x += p.vx; p.y += p.vy; } public double reverse; double fixTopZ(double a) { return a; } double getLevelHeight() { return -40; } }; class InverseSquaredRadial extends VecFunction { String getName() { return "1/r^2 single"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); ge.height = -5/r; double r3 = r*r*r; ge.div = 1/r3; ge.vecX = -xx/r3; ge.vecY = -yy/r3; } VecFunction createNext() { return new InverseSquaredRadialDouble(); } }; class InverseSquaredRadialDouble extends VecFunction { String getName() { return "1/r^2 double"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-5)*mu; double xx2 = (x*(16.0/gridsize)-11)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r1 = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double r2 = java.lang.Math.sqrt(xx2*xx2+yy*yy+.00000001); ge.height = -5/r1-5/r2; ge.div = 1/(r1*r1*r1)+1/(r2*r2*r2); ge.vecX = -xx/(r1*r1*r1)-xx2/(r2*r2*r2); ge.vecY = -yy/(r1*r1*r1)-yy/(r2*r2*r2); } VecFunction createNext() { return new InverseSquaredRadialDipole(); } }; class InverseSquaredRadialDipole extends VecFunction { String getName() { return "1/r^2 dipole"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-5)*mu; double xx2 = (x*(16.0/gridsize)-11)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r1 = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double r2 = java.lang.Math.sqrt(xx2*xx2+yy*yy+.00000001); ge.height = -5/r1+5/r2; ge.div = 1/(r1*r1*r1)-1/(r2*r2*r2); ge.vecX = -xx/(r1*r1*r1)+xx2/(r2*r2*r2); ge.vecY = -yy/(r1*r1*r1)+yy/(r2*r2*r2); } VecFunction createNext() { return new InverseSquaredRadialQuad(); } }; class InverseSquaredRadialQuad extends VecFunction { String getName() { return "1/r^2 quad"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-7)*mu; double xx2 = (x*(16.0/gridsize)-9)*mu; double yy = (y*(16.0/gridsize)-7)*mu; double yy2 = (y*(16.0/gridsize)-9)*mu; double r1 = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double r2 = java.lang.Math.sqrt(xx2*xx2+yy*yy+.00000001); double r3 = java.lang.Math.sqrt(xx*xx+yy2*yy2+.00000001); double r4 = java.lang.Math.sqrt(xx2*xx2+yy2*yy2+.00000001); ge.height = -5/r1-5/r4+5/r2+5/r3; ge.div = 1/(r1*r1*r1)+1/(r4*r4*r4)-1/(r2*r2*r2)-1/(r3*r3*r3); ge.vecX = -xx/(r1*r1*r1)-xx2/(r4*r4*r4) +xx2/(r2*r2*r2)+xx/(r3*r3*r3); ge.vecY = -yy/(r1*r1*r1)-yy2/(r4*r4*r4) +yy/(r2*r2*r2)+yy2/(r3*r3*r3); } VecFunction createNext() { return new InverseSquaredRadialSphere(); } }; class InverseSquaredRadialSphere extends VecFunction { String getName() { return "1/r^2 sphere"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double r3 = r*r*r; double a = 2.5; double alpha = 5; if (r >= a) { ge.height = -alpha/r; ge.div = alpha/r3; ge.vecX = -xx*alpha/r3; ge.vecY = -yy*alpha/r3; } else { double alphaa3 = alpha/(a*a*a); ge.height = -3*alpha/(2*a)+alphaa3*r*r/2; ge.div = -2*alphaa3; ge.vecX = -xx*alphaa3; ge.vecY = -yy*alphaa3; } ge.height *= 3; } VecFunction createNext() { return new InverseRadial(); } }; class InverseRadial extends VecFunction { String getName() { return "1/r single"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); ge.height = java.lang.Math.log(r); ge.div = (r < .1) ? 1 : 0; double r2 = r*r; ge.vecX = -xx/r2; ge.vecY = -yy/r2; } VecFunction createNext() { return new InverseRadialDouble(); } }; class InverseRadialDouble extends VecFunction { String getName() { return "1/r double"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-5)*mu; double xx2 = (x*(16.0/gridsize)-11)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r1 = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double r2 = java.lang.Math.sqrt(xx2*xx2+yy*yy+.00000001); ge.height = java.lang.Math.log(r1)+java.lang.Math.log(r2); ge.div = (r1 < .1 || r2 < .1) ? 1 : 0; ge.vecX = -xx/(r1*r1)-xx2/(r2*r2); ge.vecY = -yy/(r1*r1)-yy/(r2*r2); } VecFunction createNext() { return new InverseRadialDipole(); } }; class InverseRadialDipole extends VecFunction { String getName() { return "1/r dipole"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-5)*mu; double xx2 = (x*(16.0/gridsize)-11)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r1 = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double r2 = java.lang.Math.sqrt(xx2*xx2+yy*yy+.00000001); ge.height = java.lang.Math.log(r1)-java.lang.Math.log(r2); ge.div = (r1 < .1) ? 1 : (r2 < .1) ? -1 : 0; ge.vecX = -xx/(r1*r1)+xx2/(r2*r2); ge.vecY = -yy/(r1*r1)+yy/(r2*r2); } VecFunction createNext() { return new InverseRadialQuad(); } }; class InverseRadialQuad extends VecFunction { String getName() { return "1/r quad"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-7)*mu; double xx2 = (x*(16.0/gridsize)-9)*mu; double yy = (y*(16.0/gridsize)-7)*mu; double yy2 = (y*(16.0/gridsize)-9)*mu; double r1 = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double r2 = java.lang.Math.sqrt(xx2*xx2+yy*yy+.00000001); double r3 = java.lang.Math.sqrt(xx*xx+yy2*yy2+.00000001); double r4 = java.lang.Math.sqrt(xx2*xx2+yy2*yy2+.00000001); ge.height = +java.lang.Math.log(r1)+java.lang.Math.log(r4) -java.lang.Math.log(r2)-java.lang.Math.log(r3); ge.div = (r1 < .1) ? 1 : (r4 < .1) ? 1 : (r2 < .1) ? -1 : (r3 < .1) ? -1 : 0; ge.vecX = -xx/(r1*r1)-xx2/(r4*r4) +xx2/(r2*r2)+xx/(r3*r3); ge.vecY = -yy/(r1*r1)-yy2/(r4*r4) +yy/(r2*r2)+yy2/(r3*r3); } VecFunction createNext() { return new ConstRadial(); } }; class ConstRadial extends VecFunction { String getName() { return "const radial"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); ge.height = r/2; ge.div = -1/r; ge.vecX = -xx/r; ge.vecY = -yy/r; } VecFunction createNext() { return new LinearRadial(); } }; class LinearRadial extends VecFunction { String getName() { return "linear radial"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double r3 = r*r*r; double k = .3; ge.height = .5*k*r*r; ge.div = -2*k; ge.vecX = -xx*k; ge.vecY = -yy*k; } VecFunction createNext() { return new FiniteSheet(); } }; class FiniteSheet extends VecFunction { String getName() { return "finite sheet"; } void setGrid(GridElement ge, int x, int y) { double l = 3; double mu = .6; int i; ge.height = 0; ge.div = 0; ge.vecX = 0; ge.vecY = 0; double mult = 4/60.0; for (i = 0; i != 60; i++) { double y0 = 8+l*(i/(59.0/2.0)-1); double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-y0)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); ge.height -= java.lang.Math.log(1/r)*mult; ge.vecX -= xx*mult/(r*r); ge.vecY -= yy*mult/(r*r); if (r < .1) ge.div++; } } VecFunction createNext() { return new FiniteSheetPair(); } }; class FiniteSheetPair extends VecFunction { String getName() { return "finite sheet pair"; } void setGrid(GridElement ge, int x, int y) { double l = 3; double mu = .6; int i; ge.height = 0; ge.div = 0; ge.vecX = 0; ge.vecY = 0; double mult = 2.5/60.0; double vecmult = 1/60.0; for (i = 0; i != 60; i++) { double y0, xx, yy, r; y0 = 8+l*(i/(59.0/2.0)-1); xx = (x*(16.0/gridsize)-6)*mu; yy = (y*(16.0/gridsize)-y0)*mu; r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); ge.height -= java.lang.Math.log(1/r)*mult; ge.vecX -= xx*vecmult/(r*r); ge.vecY -= yy*vecmult/(r*r); if (r < .1) ge.div++; xx = (x*(16.0/gridsize)-10)*mu; yy = (y*(16.0/gridsize)-y0)*mu; r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); ge.height += java.lang.Math.log(1/r)*mult; ge.vecX += xx*vecmult/(r*r); ge.vecY += yy*vecmult/(r*r); if (r < .1) ge.div--; } } VecFunction createNext() { return new PendulumPotential(); } }; class PendulumPotential extends VecFunction { String getName() { return "pendulum potential"; } void setGrid(GridElement ge, int x, int y) { double xx = x*(6.2/gridsize)-3.1; double yy = y*(6.2/gridsize)-3.1; double cosx = java.lang.Math.cos(xx); double cosy = java.lang.Math.cos(yy); double sinx = java.lang.Math.sin(xx); double siny = java.lang.Math.sin(yy); ge.height = -5*cosx*cosy; ge.vecX = -sinx*cosy; ge.vecY = -cosx*siny; ge.div = -2*cosx*cosy; } VecFunction createNext() { return new OneDirectionFunction(); } }; class OneDirectionFunction extends VecFunction { String getName() { return "one direction"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-8)*mu; ge.height = -xx; ge.div = 0; ge.vecX = 1; ge.vecY = 0; } VecFunction createNext() { return new ConstantToYAxis(); } }; class ConstantToYAxis extends VecFunction { String getName() { return "constant to y axis"; } void setGrid(GridElement ge, int x, int y) { double alpha = 1; double mu = .6; double xx = (x*(16.0/gridsize)-8)*mu; ge.vecX = (xx < 0) ? alpha : -alpha; ge.vecY = 0; ge.height = -ge.vecX*xx; } VecFunction createNext() { return new LinearToYAxis(); } }; class LinearToYAxis extends VecFunction { String getName() { return "linear to y axis"; } void setGrid(GridElement ge, int x, int y) { double alpha = .3; double mu = .6; double xx = (x*(16.0/gridsize)-8)*mu; ge.vecX = -alpha*xx; ge.vecY = 0; ge.height = alpha*xx*xx; ge.div = -alpha; } VecFunction createNext() { return new InverseToYAxis(); } }; class InverseToYAxis extends VecFunction { String getName() { return "inverse to y axis"; } void setGrid(GridElement ge, int x, int y) { double alpha = 1; double mu = .6; double xx = (x*(16.0/gridsize)-8)*mu; if (xx == 0) xx = .00001; ge.vecX = -alpha/xx; ge.vecY = 0; ge.height = -.5/(xx*xx); ge.div = -ge.vecX/xx; } VecFunction createNext() { return new InverseRotational(); } }; class InverseRotational extends VecFunction { String getName() { return "1/r rotational"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); ge.div = 0; ge.curl = 0; double r2 = r*r; ge.vecX = -yy/r2; ge.vecY = xx/r2; } void moveParticle(Particle p, boolean withforce) { if (withforce) { super.moveParticle(p, withforce); return; } double mu = .6; double xx = (p.x*(16.0/gridsize)-8)*mu; double yy = (p.y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double ang = java.lang.Math.atan2(yy, xx); double angadd = .3/(r*r); ang += angadd*reverse; if (angadd > 4) ang = java.lang.Math.random() * (3.14159265*2); xx = r*java.lang.Math.cos(ang); yy = r*java.lang.Math.sin(ang); p.x = (xx/mu+8)*gridsize/16.0; p.y = (yy/mu+8)*gridsize/16.0; } boolean nonGradient() { return true; } VecFunction createNext() { return new InverseRotationalPotential(); } }; class InverseRotationalPotential extends VecFunction { String getName() { return "1/r rotational potential"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); ge.div = 0; ge.curl = 0; double r2 = r*r; ge.vecX = -yy/r2; ge.vecY = xx/r2; ge.height = -java.lang.Math.atan2(xx, -yy); } void moveParticle(Particle p, boolean withforce) { if (withforce) { super.moveParticle(p, withforce); return; } double mu = .6; double xx = (p.x*(16.0/gridsize)-8)*mu; double yy = (p.y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double ang = java.lang.Math.atan2(yy, xx); double angadd = .3/(r*r); ang += angadd*reverse; if (angadd > 4) ang = java.lang.Math.random() * (3.14159265*2); xx = r*java.lang.Math.cos(ang); yy = r*java.lang.Math.sin(ang); p.x = (xx/mu+8)*gridsize/16.0; p.y = (yy/mu+8)*gridsize/16.0; } VecFunction createNext() { return new InverseSquareRotational(); } }; class InverseSquareRotational extends VecFunction { String getName() { return "1/r^2 rotational"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.1); ge.div = 0; double r3 = r*r*r; ge.curl = -1/r3; ge.vecX = -yy/r3; ge.vecY = xx/r3; } void moveParticle(Particle p, boolean withforce) { if (withforce) { super.moveParticle(p, withforce); return; } double mu = .6; double xx = (p.x*(16.0/gridsize)-8)*mu; double yy = (p.y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double ang = java.lang.Math.atan2(yy, xx); ang += reverse/(r*r*r); xx = r*java.lang.Math.cos(ang); yy = r*java.lang.Math.sin(ang); p.x = (xx/mu+8)*gridsize/16.0; p.y = (yy/mu+8)*gridsize/16.0; } boolean nonGradient() { return true; } VecFunction createNext() { return new InverseSquareRotationalDouble(); } }; class InverseSquareRotationalDouble extends VecFunction { String getName() { return "1/r^2 rotational double"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-6)*mu; double xx2 = (x*(16.0/gridsize)-10)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.1); double r2 = java.lang.Math.sqrt(xx2*xx2+yy*yy+.1); ge.div = 0; ge.curl = -1/(r*r*r)-1/(r2*r2*r2); ge.vecX = -yy/(r*r*r)-yy/(r2*r2*r2); ge.vecY = xx/(r*r*r)+xx2/(r2*r2*r2); } void moveParticle(Particle p, boolean withforce) { if (withforce) { super.moveParticle(p, withforce); return; } double mu = .6; double xx = (p.x*(16.0/gridsize)-6)*mu; double yy = (p.y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double ang = java.lang.Math.atan2(yy, xx); ang += reverse/(r*r*r); xx = r*java.lang.Math.cos(ang); yy = r*java.lang.Math.sin(ang); double xx2 = xx-4*mu; double r2 = java.lang.Math.sqrt(xx2*xx2+yy*yy+.00000001); ang = java.lang.Math.atan2(yy, xx2); ang += reverse/(r2*r2*r2); xx2 = r2*java.lang.Math.cos(ang); yy = r2*java.lang.Math.sin(ang); p.x = (xx2/mu+10)*gridsize/16.0; p.y = (yy/mu+8)*gridsize/16.0; } boolean nonGradient() { return true; } VecFunction createNext() { return new InverseSquareRotationalDipole(); } }; class InverseSquareRotationalDipole extends VecFunction { String getName() { return "1/r^2 rotational dipole"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-6)*mu; double xx2 = (x*(16.0/gridsize)-10)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.1); double r2 = java.lang.Math.sqrt(xx2*xx2+yy*yy+.1); ge.div = 0; ge.curl = -1/(r*r*r)+1/(r2*r2*r2); ge.vecX = -yy/(r*r*r)+yy/(r2*r2*r2); ge.vecY = xx/(r*r*r)-xx2/(r2*r2*r2); } void moveParticle(Particle p, boolean withforce) { if (withforce) { super.moveParticle(p, withforce); return; } double mu = .6; double xx = (p.x*(16.0/gridsize)-6)*mu; double yy = (p.y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double ang = java.lang.Math.atan2(yy, xx); ang += reverse/(r*r*r); xx = r*java.lang.Math.cos(ang); yy = r*java.lang.Math.sin(ang); double xx2 = xx-4*mu; double r2 = java.lang.Math.sqrt(xx2*xx2+yy*yy+.00000001); ang = java.lang.Math.atan2(yy, xx2); ang -= reverse/(r2*r2*r2); xx2 = r2*java.lang.Math.cos(ang); yy = r2*java.lang.Math.sin(ang); p.x = (xx2/mu+10)*gridsize/16.0; p.y = (yy/mu+8)*gridsize/16.0; } boolean nonGradient() { return true; } VecFunction createNext() { return new LinearRotational(); } }; class LinearRotational extends VecFunction { String getName() { return "linear rotational"; } void setGrid(GridElement ge, int x, int y) { double mu = .1; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; ge.div = 0; ge.curl = 1; ge.vecX = -yy; ge.vecY = xx; } boolean nonGradient() { return true; } VecFunction createNext() { return new ConstantRotational(); } }; class ConstantRotational extends VecFunction { String getName() { return "constant rotational"; } void setGrid(GridElement ge, int x, int y) { double mu = .1; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; ge.div = 0; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); ge.curl = .1/r; ge.vecX = -yy/r; ge.vecY = xx/r; } void moveParticle(Particle p, boolean withforce) { if (withforce) { super.moveParticle(p, withforce); return; } double mu = .6; double xx = (p.x*(16.0/gridsize)-8)*mu; double yy = (p.y*(16.0/gridsize)-8)*mu; double r = java.lang.Math.sqrt(xx*xx+yy*yy+.00000001); double ang = java.lang.Math.atan2(yy, xx); ang += .2*reverse/r; xx = r*java.lang.Math.cos(ang); yy = r*java.lang.Math.sin(ang); p.x = (xx/mu+8)*gridsize/16.0; p.y = (yy/mu+8)*gridsize/16.0; } boolean nonGradient() { return true; } VecFunction createNext() { return new FxEqualsYField(); } }; class FxEqualsYField extends VecFunction { String getName() { return "fx=y"; } void setGrid(GridElement ge, int x, int y) { double mu = .1; double yy = (y*(16.0/gridsize)-8)*mu; ge.div = 0; ge.curl = -mu; ge.vecX = yy; ge.vecY = 0; } boolean nonGradient() { return true; } VecFunction createNext() { return new FxEqualsY2Field(); } }; class FxEqualsY2Field extends VecFunction { String getName() { return "fx=y2"; } void setGrid(GridElement ge, int x, int y) { double mu = .1; double yy = (y*(16.0/gridsize)-8)*mu; ge.div = 0; ge.curl = -2*yy; ge.vecX = yy*yy; ge.vecY = 0; } boolean nonGradient() { return true; } VecFunction createNext() { return new SaddlePotential(); } }; class SaddlePotential extends VecFunction { String getName() { return "saddle"; } void setGrid(GridElement ge, int x, int y) { double mu = .4; double xx = (x*(4.0/gridsize)-2)*mu; double yy = (y*(4.0/gridsize)-2)*mu; ge.height = (2*xx*xx-yy*yy) * 10; ge.div = 0; ge.vecX = -xx; ge.vecY = yy*.5; } VecFunction createNext() { return new RotationalExpansion(); } double fixTopZ(double a) { return -20; } double getLevelHeight() { return -20; } }; class RotationalExpansion extends VecFunction { String getName() { return "rotation+expansion"; } void setGrid(GridElement ge, int x, int y) { double mu = .1; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; ge.div = 2; ge.curl = 2; ge.vecX = xx-yy; ge.vecY = xx+yy; } boolean nonGradient() { return true; } VecFunction createNext() { return new Function4Field(); } }; class Function4Field extends VecFunction { String getName() { return "(x2-y,x+y2)"; } void setGrid(GridElement ge, int x, int y) { double mu = .1; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; ge.div = 2*xx+2*yy; ge.curl = 2; ge.vecX = (xx*xx-yy)*.4; ge.vecY = (xx+yy*yy)*.4; } boolean nonGradient() { return true; } VecFunction createNext() { return new Function5Field(); } }; class Function5Field extends VecFunction { String getName() { return "(x+y2,x2-y)"; } void setGrid(GridElement ge, int x, int y) { double mu = .1; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; ge.div = 0; ge.curl = xx-yy; ge.vecX = (xx+yy*yy)*.4; ge.vecY = (xx*xx-yy)*.4; } boolean nonGradient() { return true; } VecFunction createNext() { return new Function6Field(); } }; class Function6Field extends VecFunction { String getName() { return "(x,x2)"; } void setGrid(GridElement ge, int x, int y) { double mu = .1; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; ge.div = 1; ge.curl = 2*xx; ge.vecX = xx; ge.vecY = xx*xx; } boolean nonGradient() { return true; } VecFunction createNext() { return new Function7Field(); } }; class Function7Field extends VecFunction { String getName() { return "u=x2+y"; } void setGrid(GridElement ge, int x, int y) { double mu = .1; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; ge.height = (xx*xx+yy)*5; ge.div = -2; ge.vecX = -2*xx; ge.vecY = -1; } VecFunction createNext() { return new Function8Field(); } }; class Function8Field extends VecFunction { String getName() { return "sin(r2)/r2"; } void setGrid(GridElement ge, int x, int y) { double mu = .6; double xx = (x*(16.0/gridsize)-8)*mu; double yy = (y*(16.0/gridsize)-8)*mu; double r2 = xx*xx+yy*yy+.00000001; ge.height = 5*java.lang.Math.sin(r2)/r2; double r = java.lang.Math.sqrt(r2); double sinr2 = java.lang.Math.sin(r2); double cosr2 = java.lang.Math.cos(r2); double r3 = r2*r; double r4 = r2*r2; double vecR = (sinr2/r4-cosr2/r2)*.4; ge.vecX = xx*vecR; ge.vecY = yy*vecR; ge.div = 4*(cosr2/r2-sinr2/r4+sinr2); } VecFunction createNext() { return null; } };