// base.java (C) 2001 by Paul Falstad, www.falstad.com
// this file must be run through cpp with one of BUILD_E, BUILD_V,
// BUILD_M defined before compiling with java compiler. This was done
// to reduce class file sizes.
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.*;
#ifdef BUILD_E
#define BUILD_CASE_EMV(E,M,V) E
#elif defined(BUILD_M)
#define BUILD_CASE_EMV(E,M,V) M
#else
#define BUILD_CASE_EMV(E,M,V) V
#endif
class Vec3DemoCanvas extends Canvas {
Vec3DemoFrame pg;
Vec3DemoCanvas(Vec3DemoFrame p) {
pg = p;
}
public Dimension getPreferredSize() {
return new Dimension(300,400);
}
public void update(Graphics g) {
pg.updateVec3Demo(g);
}
public void paint(Graphics g) {
pg.updateVec3Demo(g);
}
};
class Vec3DemoLayout implements LayoutManager {
public Vec3DemoLayout() {}
public void addLayoutComponent(String name, Component c) {}
public void removeLayoutComponent(Component c) {}
public Dimension preferredLayoutSize(Container target) {
return new Dimension(500, 500);
}
public Dimension minimumLayoutSize(Container target) {
return new Dimension(100,100);
}
public void layoutContainer(Container target) {
int barwidth = 0;
int i;
for (i = 1; i < target.getComponentCount(); i++) {
Component m = target.getComponent(i);
if (m.isVisible()) {
Dimension d = m.getPreferredSize();
if (d.width > barwidth)
barwidth = d.width;
}
}
Insets insets = target.insets();
int targetw = target.size().width - insets.left - insets.right;
int cw = targetw-barwidth;
int targeth = target.size().height - (insets.top+insets.bottom);
target.getComponent(0).move(insets.left, insets.top);
target.getComponent(0).resize(cw, targeth);
cw += insets.left;
int h = insets.top;
for (i = 1; i < target.getComponentCount(); i++) {
Component m = target.getComponent(i);
if (m.isVisible()) {
Dimension d = m.getPreferredSize();
if (m instanceof Scrollbar || m instanceof TextComponent)
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 Vec3Demo extends Applet implements ComponentListener {
static Vec3DemoFrame 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 Vec3DemoFrame(null);
ogf.init();
}
void showFrame() {
if (ogf == null) {
started = true;
ogf = new Vec3DemoFrame(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 Vec3DemoFrame extends Frame
implements ComponentListener, ActionListener, AdjustmentListener,
MouseMotionListener, MouseListener, ItemListener {
Thread engine = null;
Dimension winSize;
Rectangle viewMain, viewAxes;
Image dbimage;
Vec3Demo applet;
Random random;
public String getAppletInfo() {
return "Vec3Demo 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;
}
Vec3DemoCanvas cv;
Checkbox stoppedCheck;
Button resetButton;
Button kickButton;
Checkbox reverseCheck;
Button infoButton;
Choice functionChooser;
Choice dispChooser;
#ifndef BUILD_M
static final int DISP_PART_VELOC = 0;
static final int DISP_PART_FORCE = 1;
static final int DISP_VECTORS = 2;
static final int DISP_LINES = 3;
static final int DISP_EQUIPS = 4;
static final int DISP_PART_VELOC_A = -1;
static final int DISP_VECTORS_A = -2;
static final int DISP_PART_MAG = -3;
static final int DISP_VIEW_PAPER = -4;
#else
static final int DISP_PART_VELOC = 0;
static final int DISP_PART_VELOC_A = 1;
static final int DISP_VECTORS = 2;
static final int DISP_VECTORS_A = 3;
static final int DISP_LINES = 4;
static final int DISP_PART_MAG = 5;
static final int DISP_VIEW_PAPER = 6;
static final int DISP_EQUIPS = -1;
static final int DISP_PART_FORCE = -4;
#endif
Choice sliceChooser;
static final int SLICE_NONE = 0;
static final int SLICE_X = 1;
static final int SLICE_Y = 2;
static final int SLICE_Z = 3;
Label partCountLabel;
Label textFieldLabel;
Label strengthLabel;
Scrollbar partCountBar;
Scrollbar strengthBar;
Scrollbar aux1Bar;
Scrollbar aux2Bar;
Scrollbar aux3Bar;
double fieldStrength, partMult;
Color darkYellow = new Color(144, 144, 0);
final double lineWidth = .01;
class AuxBar {
Scrollbar bar;
Label label;
AuxBar(Label l, Scrollbar b) { label = l; bar = b; }
};
AuxBar auxBars[];
Label vecDensityLabel;
Scrollbar vecDensityBar;
Label potentialLabel;
Scrollbar potentialBar;
Label lineDensityLabel;
Scrollbar lineDensityBar;
Choice modeChooser;
TextField textFields[];
static final int MODE_ANGLE = 0;
static final int MODE_ZOOM = 1;
static final int MODE_SLICE = 2;
int reverse;
int xpoints[];
int ypoints[];
int slicerPoints[][];
double sliceFaces[][];
double sliceFace[];
Particle particles[];
FieldVector vectors[];
int vecCount;
int density[][][];
double sliceval = 0;
double rotmatrix[];
double cameraPos[];
double intersection[];
double intersectionDistance;
int vectorSpacing = 16;
int currentStep;
boolean selectedSlice, mouseDown, getPot;
boolean showA;
boolean parseError;
Color fieldColors[];
Color equipColors[];
// was 1./5, 10; 1./3, 6 worked better
static final double densitygroupsize = 1./2;
static final int densitygridsize = 4;
static final int maxParticleCount = 5000;
double zoom = 3;
boolean dragging;
int oldDragX, oldDragY, dragX, dragY, dragStartX, dragStartY;
double dragZoomStart, lastXRot, lastYRot;
Vector functionList;
VecFunction curfunc;
int pause = 20;
Vec3DemoFrame(Vec3Demo a) {
super("3-D Vector Fields Applet v1.3c");
applet = a;
}
public void init() {
try {
String param = applet.getParameter("PAUSE");
if (param != null)
pause = Integer.parseInt(param);
} catch (Exception e) { }
functionList = new Vector();
VecFunction vf = new
BUILD_CASE_EMV(InverseSquaredRadial(),
InverseRotational(),
InverseSquaredRadial());
while (vf != null) {
functionList.addElement(vf);
vf = vf.createNext();
}
random = new Random();
particles = new Particle[maxParticleCount];
int i;
for (i = 0; i != maxParticleCount; i++)
particles[i] = new Particle();
xpoints = new int[4];
ypoints = new int[4];
slicerPoints = new int[2][5*2];
sliceFaces = new double[4][3];
rotmatrix = new double[9];
setXYView();
density = new int[densitygridsize][densitygridsize][densitygridsize];
setLayout(new Vec3DemoLayout());
cv = new Vec3DemoCanvas(this);
cv.addComponentListener(this);
cv.addMouseMotionListener(this);
cv.addMouseListener(this);
add(cv);
/*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);
functionChooser.addItemListener(this);
dispChooser = new Choice();
dispChooser.addItemListener(this);
setupDispChooser(true);
add(dispChooser);
modeChooser = new Choice();
modeChooser.add("Mouse = Adjust Angle");
modeChooser.add("Mouse = Adjust Zoom");
modeChooser.addItemListener(this);
add(modeChooser);
sliceChooser = new Choice();
sliceChooser.add("No Slicing");
sliceChooser.add("Show X Slice");
sliceChooser.add("Show Y Slice");
sliceChooser.add("Show Z Slice");
sliceChooser.addItemListener(this);
add(sliceChooser);
stoppedCheck = new Checkbox("Stopped");
stoppedCheck.addItemListener(this);
add(stoppedCheck);
reverseCheck = new Checkbox("Reverse");
reverseCheck.addItemListener(this);
add(reverseCheck);
resetButton = new Button("Reset");
add(resetButton);
resetButton.addActionListener(this);
kickButton = new Button("Kick");
#ifndef BUILD_M
add(kickButton);
kickButton.addActionListener(this);
kickButton.disable();
#endif
add(strengthLabel = new Label("Field Strength", Label.CENTER));
add(strengthBar = new Scrollbar(Scrollbar.HORIZONTAL, 10, 1, 0, 100));
strengthBar.addAdjustmentListener(this);
add(partCountLabel = new Label("Number of Particles", Label.CENTER));
add(partCountBar = new Scrollbar(Scrollbar.HORIZONTAL,
500, 1, 1,
maxParticleCount));
partCountBar.addAdjustmentListener(this);
add(vecDensityLabel = new Label("Vector Density", Label.CENTER));
add(vecDensityBar = new Scrollbar(Scrollbar.HORIZONTAL, 16, 1, 2, 64));
vecDensityBar.addAdjustmentListener(this);
add(lineDensityLabel = new Label(
#ifdef BUILD_V
"Streamline Density",
#else
"Field Line Density",
#endif
Label.CENTER));
add(lineDensityBar = new Scrollbar(Scrollbar.HORIZONTAL, 5, 1, 3, 16));
lineDensityBar.addAdjustmentListener(this);
add(potentialLabel = new Label("Potential", Label.CENTER));
add(potentialBar = new Scrollbar(Scrollbar.HORIZONTAL, 250,
1, 0, 1000));
potentialBar.addAdjustmentListener(this);
Label lb;
auxBars = new AuxBar[3];
add(lb = new Label("Aux 1", Label.CENTER));
add(aux1Bar = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 100));
aux1Bar.addAdjustmentListener(this);
auxBars[0] = new AuxBar(lb, aux1Bar);
add(lb = new Label("Aux 2", Label.CENTER));
add(aux2Bar = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 100));
aux2Bar.addAdjustmentListener(this);
auxBars[1] = new AuxBar(lb, aux2Bar);
add(lb = new Label("Aux 3", Label.CENTER));
add(aux3Bar = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 100));
aux3Bar.addAdjustmentListener(this);
auxBars[2] = new AuxBar(lb, aux3Bar);
#ifdef BUILD_V
add(textFieldLabel = new Label("", Label.CENTER));
#endif
textFields = new TextField[3];
for (i = 0; i != 3; i++) {
add(textFields[i] = new TextField());
textFields[i].addActionListener(this);
}
fieldColors = new Color[513];
for (i = 0; i != 256; i++) {
int col = (255<<24) | (i<<8);
fieldColors[i] = new Color(col);
}
for (i = 0; i != 256; i++) {
int col = (255<<24) | (255<<8) | (i * (0x10001));
fieldColors[i+256] = new Color(col);
}
fieldColors[512] = fieldColors[511];
equipColors = new Color[513];
for (i = 0; i != 256; i++) {
int r = 255-i/2;
int gb = i/2;
int col = (255<<24) | (r<<16) | (gb<<8) | gb;
equipColors[i] = new Color(col);
}
for (i = 0; i != 256; i++) {
int g = 128+i/2;
int rb = 128-i/2;
int col = (255<<24) | (rb<<16) | (g<<8) | rb;
equipColors[i+256] = new Color(col);
}
equipColors[512] = equipColors[511];
add(new Label("", Label.CENTER));
intersection = new double[3];
reinit();
cv.setBackground(Color.black);
cv.setForeground(Color.lightGray);
resize(500, 500);
handleResize();
Dimension screen = getToolkit().getScreenSize();
Dimension x = getSize();
setLocation((screen.width - x.width)/2,
(screen.height - x.height)/2);
functionChanged();
dispChooserChanged();
show();
requestFocus();
}
void setViewMatrix(double a, double b) {
int i;
for (i = 0; i != 9; i++)
rotmatrix[i] = 0;
rotmatrix[0] = rotmatrix[4] = rotmatrix[8] = 1;
rotate(a, b);
lastXRot = lastYRot = 0;
}
void setXYView() {
setViewMatrix(0, pi/11);
}
void setXYViewExact() {
setViewMatrix(0, 0);
}
void setXZView() {
setViewMatrix(0, pi/11-pi/2);
}
void setXZViewExact() {
setViewMatrix(0, -pi/2);
}
void handleResize() {
Dimension d = winSize = cv.getSize();
if (winSize.width == 0)
return;
dbimage = createImage(d.width, d.height);
scaleworld();
viewMain = new Rectangle(winSize);
viewAxes = new Rectangle(winSize.width-100, 0, 100, 100);
}
void resetDensityGroups() {
int i, j, k;
for (i = 0; i != densitygridsize; i++)
for (j = 0; j != densitygridsize; j++)
for (k = 0; k != densitygridsize; k++)
density[i][j][k] = 0;
int slice = sliceChooser.getSelectedIndex();
boolean sliced = (slice > 0);
int pcount = getParticleCount();
for (i = 0; i != pcount; i++) {
Particle p = particles[i];
if (sliced)
p.pos[slice-SLICE_X] = sliceval;
addToDensityGroup(p);
}
// invalidate all unused particles so they will be
// repositioned if the particle slider is moved to the right,
// rather than just falling in where they happened to be last time
for (; i != maxParticleCount; i++) {
Particle p = particles[i];
p.lifetime = -100;
}
}
int addToDensityGroup(Particle p) {
int a = (int)((p.pos[0]+1)*(densitygridsize/2));
int b = (int)((p.pos[1]+1)*(densitygridsize/2));
int c = (int)((p.pos[2]+1)*(densitygridsize/2));
int n = 0;
try {
n = ++density[a][b][c];
if (n > maxParticleCount)
System.out.print(a + " " + b + " " + c + " " + density[a][b][c] + "\n");
} catch (Exception e) {
System.out.print(p.pos[0] + " " + p.pos[1] + " " + p.pos[2] + "\n");
e.printStackTrace();
}
return n;
}
void removeFromDensityGroup(Particle p) {
int a = (int)((p.pos[0]+1)*(densitygridsize/2));
int b = (int)((p.pos[1]+1)*(densitygridsize/2));
int c = (int)((p.pos[2]+1)*(densitygridsize/2));
try {
if (--density[a][b][c] < 0)
System.out.print(a + " " + b + " " + c + " " + density[a][b][c] + "\n");
} catch (Exception e) {
System.out.print(p.pos[0] + " " + p.pos[1] + " " + p.pos[2] + "\n");
e.printStackTrace();
}
}
void positionParticle(Particle p) {
int x, y, z;
int bestx = 0, besty = 0, bestz = 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);
int randaddz = getrand(densitygridsize);
for (x = 0; x != densitygridsize; x++)
for (y = 0; y != densitygridsize; y++)
for (z = 0; z != densitygridsize; z++) {
int ix = (randaddx + x) % densitygridsize;
int iy = (randaddy + y) % densitygridsize;
int iz = (randaddz + z) % densitygridsize;
if (density[ix][iy][iz] <= best) {
bestx = ix;
besty = iy;
bestz = iz;
best = density[ix][iy][iz];
}
}
p.pos[0] = bestx*densitygroupsize +
getrand(100)*densitygroupsize/100.0 - 1;
p.pos[1] = besty*densitygroupsize +
getrand(100)*densitygroupsize/100.0 - 1;
p.pos[2] = bestz*densitygroupsize +
getrand(100)*densitygroupsize/100.0 - 1;
p.lifetime = curfunc.redistribute() ? 500 : 5000;
p.stepsize = 1;
p.theta = (getrand(101)-50)*pi/50.;
p.phi = (getrand(101)-50)*pi/50.;
int j;
for (j = 0; j != 3; j++)
p.vel[j] = 0;
}
int getParticleCount() {
return partCountBar.getValue();
}
void resetParticles() {
int pcount = getParticleCount();
int i, j;
for (i = 0; i != pcount; i++) {
Particle p = particles[i];
for (j = 0; j != 3; j++) {
p.pos[j] = getrand(200)/100.0 - 1;
p.vel[j] = 0;
}
p.lifetime = i*2;
p.stepsize = 1;
}
resetDensityGroups();
}
void kickParticles() {
int i, j;
for (i = 0; i != getParticleCount(); i++) {
Particle p = particles[i];
for (j = 0; j != 3; j++)
p.vel[j] += (getrand(100)/99.0 - .5) * .04;
}
}
// multiply rotation matrix by rotations through angle1 and angle2
void rotate(double angle1, double angle2) {
double r1cos = java.lang.Math.cos(angle1);
double r1sin = java.lang.Math.sin(angle1);
double r2cos = java.lang.Math.cos(angle2);
double r2sin = java.lang.Math.sin(angle2);
double rotm2[] = new double[9];
// angle1 is angle about y axis, angle2 is angle about x axis
rotm2[0] = r1cos;
rotm2[1] = -r1sin*r2sin;
rotm2[2] = r2cos*r1sin;
rotm2[3] = 0;
rotm2[4] = r2cos;
rotm2[5] = r2sin;
rotm2[6] = -r1sin;
rotm2[7] = -r1cos*r2sin;
rotm2[8] = r1cos*r2cos;
rotate(rotm2);
}
void rotate(double rotm2[]) {
double rotm1[] = rotmatrix;
rotmatrix = new double[9];
int i, j, k;
for (j = 0; j != 3; j++)
for (i = 0; i != 3; i++) {
double v = 0;
for (k = 0; k != 3; k++)
v += rotm1[k+j*3]*rotm2[i+k*3];
rotmatrix[i+j*3] = v;
}
}
void reinit() {
handleResize();
resetParticles();
}
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;
static final double viewDistance = 5;
void map3d(double x, double y, double z, int xpoints[],
int ypoints[], int pt) {
map3d(x, y, z, xpoints, ypoints, pt, viewMain);
}
void map3d(double x, double y, double z,
int xpoints[], int ypoints[], int pt, Rectangle view) {
double rotm[] = rotmatrix;
double realx = x*rotm[0] + y*rotm[3] + z*rotm[6];
double realy = x*rotm[1] + y*rotm[4] + z*rotm[7];
double realz = viewDistance-(x*rotm[2] + y*rotm[5] + z*rotm[8]);
double scalex = view.width*zoom/2;
double scaley = view.height*zoom/2;
double aratio = view.width/(double) view.height;
// preserve aspect ratio regardless of window dimensions
if (aratio < 1)
scaley *= aratio;
else
scalex /= aratio;
xpoints[pt] = view.x + view.width /2 + (int) (scalex*realx/realz);
ypoints[pt] = view.y + view.height/2 - (int) (scaley*realy/realz);
}
double getScalingFactor(double x, double y, double z) {
double rotm[] = rotmatrix;
double realz = viewDistance-(x*rotm[2] + y*rotm[5] + z*rotm[8]);
double scalex = winSize.width*zoom/2;
double scaley = winSize.height*zoom/2;
double aratio = winSize.width/(double) winSize.height;
// preserve aspect ratio regardless of window dimensions
if (aratio < 1)
scaley *= aratio;
else
scalex /= aratio;
// scalex and scaley should now be approx. equal
return scalex/realz;
}
// map point on screen to 3-d coordinates assuming given z depth
void unmap3d(double x3[], int x, int y, double z, Rectangle view) {
double scalex = view.width*zoom/2;
double scaley = view.height*zoom/2;
double aratio = view.width/(double) view.height;
// preserve aspect ratio regardless of window dimensions
if (aratio < 1)
scaley *= aratio;
else
scalex /= aratio;
double realz = viewDistance-z;
double realx = (x-(view.width/2))*realz/scalex;
double realy = -(y-(view.height/2))*realz/scaley;
double rotm[] = rotmatrix;
x3[0] = (realx*rotm[0] + realy*rotm[1] + z*rotm[2]);
x3[1] = (realx*rotm[3] + realy*rotm[4] + z*rotm[5]);
x3[2] = (realx*rotm[6] + realy*rotm[7] + z*rotm[8]);
}
// map point on screen to 3-d coordinates assuming it lies on a given plane
void unmap3d(double x3[], int x, int y, double pn[], double pp[],
Rectangle view) {
// first, find all points which map to (x,y) on the screen.
// this is a line.
double scalex = view.width*zoom/2;
double scaley = view.height*zoom/2;
double aratio = view.width/(double) view.height;
// preserve aspect ratio regardless of window dimensions
if (aratio < 1)
scaley *= aratio;
else
scalex /= aratio;
double vx = (x-(view.width/2))/scalex;
double vy = -(y-(view.height/2))/scaley;
// vz = -1
// map the line vector to object space (we know where the camera
// is in object space already so no need to map that)
double rotm[] = rotmatrix;
double mvx = (vx*rotm[0] + vy*rotm[1] - rotm[2]);
double mvy = (vx*rotm[3] + vy*rotm[4] - rotm[5]);
double mvz = (vx*rotm[6] + vy*rotm[7] - rotm[8]);
// calculate the intersection between the line and the given plane
double t = ((pp[0]-cameraPos[0])*pn[0] +
(pp[1]-cameraPos[1])*pn[1] +
(pp[2]-cameraPos[2])*pn[2]) /
(pn[0]*mvx+pn[1]*mvy+pn[2]*mvz);
x3[0] = cameraPos[0]+mvx*t;
x3[1] = cameraPos[1]+mvy*t;
x3[2] = cameraPos[2]+mvz*t;
}
void scaleworld() {
scalex = winSize.width/2;
scaley = winSize.height/2;
}
long lastTime;
double timeStep;
public void updateVec3Demo(Graphics realg) {
Graphics g = dbimage.getGraphics();
if (winSize == null || winSize.width == 0)
return;
if (xpoints == null)
return;
g.setColor(cv.getBackground());
g.fillRect(0, 0, winSize.width, winSize.height);
g.setColor(cv.getForeground());
// XXX make it true, then fix it so current animation works w/ vectors
boolean allquiet = false;
curfunc.setupFrame();
int disp = dispChooser.getSelectedIndex();
timeStep = 1;
if (!stoppedCheck.getState()) {
if (lastTime > 0)
timeStep = (System.currentTimeMillis()-lastTime)*.03;
if (timeStep > 3)
timeStep = 3;
lastTime = System.currentTimeMillis();
if (disp != DISP_VECTORS && disp != DISP_VECTORS_A &&
disp != DISP_LINES && disp != DISP_EQUIPS) {
moveParticles();
allquiet = false;
}
currentStep = (int)
(reverse*(lastTime/30) % 800);
if (currentStep < 0)
currentStep += 800;
} else {
lastXRot = lastYRot = 0;
lastTime = 0;
}
drawCube(g, true);
cameraPos = new double[3];
unmap3d(cameraPos, winSize.width/2, winSize.height/2, viewDistance,
viewMain);
if (disp == DISP_VECTORS || disp == DISP_VECTORS_A)
drawVectors(g);
else if (disp == DISP_LINES) {
genLines();
drawLines(g);
#ifndef BUILD_M
} else if (disp == DISP_EQUIPS) {
genEquips();
drawLines(g);
#endif
} else if (disp == DISP_VIEW_PAPER)
drawViewPaper(g);
else
drawParticles(g);
g.setColor(Color.gray);
drawCube(g, false);
drawAxes(g);
curfunc.finishFrame();
if (parseError)
centerString(g, "Can't parse expression", winSize.height-20);
//g.drawString("FR: " + framerate, 0, 10);
realg.drawImage(dbimage, 0, 0, this);
long t = System.currentTimeMillis();
frames++;
if (firsttime == 0)
firsttime = t;
else if (t-firsttime > 1000) {
framerate = frames;
firsttime = t;
frames = 0;
}
if (mouseDown)
lastXRot = lastYRot = 0;
else if (lastXRot != 0 || lastYRot != 0) {
rotate(lastXRot*timeStep, lastYRot*timeStep);
allquiet = false;
}
if (!stoppedCheck.getState() && !allquiet)
cv.repaint(pause);
}
void drawCurrentArrow(Graphics g, int x1, int y1, int x2, int y2) {
if (reverse == 1)
drawArrow(g, null, x1, y1, x2, y2, 7);
else
drawArrow(g, null, x2, y2, x1, y1, 7);
}
void drawCurrentLine(Graphics g, int x1, int y1, int x2, int y2, int n,
boolean doArrow, int dir) {
int i;
if (dir == -1) {
int x3 = x1;
int y3 = y1;
x1 = x2; y1 = y2;
x2 = x3; y2 = y3;
}
int x0 = x1;
int y0 = y1;
n *= 3;
for (i = 1; i <= n; i++) {
int x = (x2-x1)*i/n + x1;
int y = (y2-y1)*i/n + y1;
g.setColor(Color.yellow);
if (i == n && doArrow && reverse == 1)
drawCurrentArrow(g, x0, y0, x, y);
else if (i == 1 && doArrow && reverse == -1)
drawCurrentArrow(g, x0, y0, x, y);
else {
g.setColor(getCurrentColor(i));
g.drawLine(x0, y0, x, y);
}
x0 = x; y0 = y;
}
}
Color getCurrentColor(int i) {
return (((currentStep/2+400-i) & 4) > 0) ?
Color.yellow : Color.darkGray;
}
void drawSphere(Graphics g, double r, boolean back) {
int i;
int ct = 10;
for (i = 0; i != ct; i++) {
double th1 = pi*2*i/ct;
double th2 = pi*2*(i+1)/ct;
double sinth1 = r*java.lang.Math.sin(th1);
double costh1 = r*java.lang.Math.cos(th1);
double sinth2 = r*java.lang.Math.sin(th2);
double costh2 = r*java.lang.Math.cos(th2);
if (backFacing(costh1, sinth1, 0, costh1, sinth1, 0) == back) {
map3d(costh1, sinth1, 0, xpoints, ypoints, 0);
map3d(costh2, sinth2, 0, xpoints, ypoints, 1);
g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
}
if (backFacing(0, costh1, sinth1, 0, costh1, sinth1) == back) {
map3d(0, costh1, sinth1, xpoints, ypoints, 0);
map3d(0, costh2, sinth2, xpoints, ypoints, 1);
g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
}
if (backFacing(costh1, 0, sinth1, costh1, 0, sinth1) == back) {
map3d(costh1, 0, sinth1, xpoints, ypoints, 0);
map3d(costh2, 0, sinth2, xpoints, ypoints, 1);
g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
}
}
}
void fillSphere(Graphics g, double r, double xoff) {
int i, j;
int ct = 20;
for (i = 0; i != ct; i++) {
double th1 = pi*i/ct;
double th2 = pi*(i+1)/ct;
double costh1 = r*java.lang.Math.cos(th1);
double sinth1 = r*java.lang.Math.sin(th1);
double costh2 = r*java.lang.Math.cos(th2);
double sinth2 = r*java.lang.Math.sin(th2);
double cosph1 = 1, sinph1 = 0;
for (j = 0; j != ct; j++) {
double ph2 = 2*pi*(j+1)/ct;
double cosph2 = java.lang.Math.cos(ph2);
double sinph2 = java.lang.Math.sin(ph2);
double x1 = sinth1*cosph1;
double y1 = sinth1*sinph1;
double z1 = costh1;
double x = cameraPos[0]-(x1+xoff);
double y = cameraPos[1]-y1;
double z = cameraPos[2]-z1;
double d = x*x1+y*y1+z*z1;
if (d > 0) {
int dd = (int) (d/r*40);
if (dd > 255)
dd = 255;
g.setColor(new Color(dd, dd, 0));
map3d(xoff+x1, y1, z1, xpoints, ypoints, 0);
map3d(xoff+sinth1*cosph2, sinth1*sinph2, costh1,
xpoints, ypoints, 1);
map3d(xoff+sinth2*cosph2, sinth2*sinph2, costh2,
xpoints, ypoints, 2);
map3d(xoff+sinth2*cosph1, sinth2*sinph1, costh2,
xpoints, ypoints, 3);
g.fillPolygon(xpoints, ypoints, 4);
}
cosph1 = cosph2; sinph1 = sinph2;
}
}
}
void drawCylinder(Graphics g, double r, double xoff, boolean back) {
int i;
int ct = 10;
for (i = 0; i != ct; i++) {
double th1 = pi*2*i/ct;
double th2 = pi*2*(i+1)/ct;
double sinth1 = r*java.lang.Math.sin(th1);
double costh1 = r*java.lang.Math.cos(th1);
double sinth2 = r*java.lang.Math.sin(th2);
double costh2 = r*java.lang.Math.cos(th2);
if (backFacing(costh1, sinth1, 0, costh1, sinth1, 0) == back) {
map3d(xoff+costh1, sinth1, -1, xpoints, ypoints, 0);
map3d(xoff+costh2, sinth2, -1, xpoints, ypoints, 1);
map3d(xoff+costh2, sinth2, +1, xpoints, ypoints, 2);
map3d(xoff+costh1, sinth1, +1, xpoints, ypoints, 3);
g.drawPolygon(xpoints, ypoints, 4);
}
}
}
void setFaceColor(Graphics g, double d) {
int dd = 32+(int) (d*40);
if (dd > 255)
dd = 255;
g.setColor(new Color(dd, dd, 0));
}
void fillCylinder(Graphics g, double r, double xoff) {
int i;
int ct = 30;
int sidepoints[][];
sidepoints = new int[4][ct];
for (i = 0; i != ct; i++) {
double th1 = pi*2*i/ct;
double th2 = pi*2*(i+1)/ct;
double sinth1 = r*java.lang.Math.sin(th1);
double costh1 = r*java.lang.Math.cos(th1);
double sinth2 = r*java.lang.Math.sin(th2);
double costh2 = r*java.lang.Math.cos(th2);
double x = cameraPos[0]-(xoff+costh1);
double y = cameraPos[1]-sinth1;
double d = x*costh1+y*sinth1;
if (d > 0)
setFaceColor(g, d/r);
map3d(xoff+costh1, sinth1, -1, xpoints, ypoints, 0);
map3d(xoff+costh2, sinth2, -1, xpoints, ypoints, 1);
map3d(xoff+costh2, sinth2, +1, xpoints, ypoints, 2);
map3d(xoff+costh1, sinth1, +1, xpoints, ypoints, 3);
sidepoints[0][i] = xpoints[0];
sidepoints[1][i] = ypoints[0];
sidepoints[2][i] = xpoints[3];
sidepoints[3][i] = ypoints[3];
if (d > 0)
g.fillPolygon(xpoints, ypoints, 4);
}
if (!backFacing(0, 0, 1, 0, 0, 1)) {
setFaceColor(g, cameraPos[2]);
g.fillPolygon(sidepoints[2], sidepoints[3], ct);
} else if (!backFacing(0, 0, -1, 0, 0, -1)) {
setFaceColor(g, -cameraPos[2]);
g.fillPolygon(sidepoints[0], sidepoints[1], ct);
}
}
void drawAxes(Graphics g) {
g.setColor(Color.white);
map3d(0, 0, 0, xpoints, ypoints, 0, viewAxes);
map3d(1, 0, 0, xpoints, ypoints, 1, viewAxes);
drawArrow(g, "x", xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
map3d(0, 1, 0, xpoints, ypoints, 1, viewAxes);
drawArrow(g, "y", xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
map3d(0, 0, 1, xpoints, ypoints, 1, viewAxes);
drawArrow(g, "z", xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
}
void drawViewPaper(Graphics g) {
int i, j;
int ct = vecDensityBar.getValue();
ct = 24+(ct*56/64);
double z = sliceval;
double pos[] = new double[3];
double field[] = new double[3];
int slice = sliceChooser.getSelectedIndex()-SLICE_X;
if (slice < 0)
slice = 0;
int coord1 = (slice == 0) ? 1 : 0;
int coord2 = (slice == 2) ? 1 : 2;
for (i = 0; i != ct; i++) {
double x1 = i*2./ct - 1;
double x2 = (i+1.)*2/ct - 1;
for (j = 0; j != ct; j++) {
double y1 = j*2./ct - 1;
double y2 = (j+1.)*2/ct - 1;
pos[coord1] = x1;
pos[coord2] = y1;
pos[slice] = z;
curfunc.getField(field, pos);
// paper is dark when field is perpendicular, light when
// it is parallel
double prp = field[slice] < 0 ? -field[slice] : field[slice];
double par = java.lang.Math.sqrt(field[coord1]*field[coord1]+
field[coord2]*field[coord2]);
int dd = (int) ((par/2-prp)*strengthBar.getValue()*20000.+128);
if (dd < 0)
dd = 0;
if (dd > 255)
dd = 255;
g.setColor(new Color(0, dd, 0));
map3d(pos[0], pos[1], pos[2], xpoints, ypoints, 0);
pos[coord1] = x2;
map3d(pos[0], pos[1], pos[2], xpoints, ypoints, 1);
pos[coord2] = y2;
map3d(pos[0], pos[1], pos[2], xpoints, ypoints, 2);
pos[coord1] = x1;
map3d(pos[0], pos[1], pos[2], xpoints, ypoints, 3);
g.fillPolygon(xpoints, ypoints, 4);
}
}
}
void drawVectors(Graphics g) {
int x, y, z;
DrawData dd = new DrawData();
dd.mult = strengthBar.getValue() * 80.;
dd.g = g;
dd.field = new double[3];
dd.vv = new double[3];
vectorSpacing = vecDensityBar.getValue();
int slice = sliceChooser.getSelectedIndex();
boolean sliced = (slice > 0);
double vec[] = new double[3];
if (vectors == null && sliced)
vectors = new FieldVector[vectorSpacing*vectorSpacing];
vecCount = 0;
if (!sliced) {
vectorSpacing /= 2;
if (vectors == null)
vectors =
new FieldVector[vectorSpacing*vectorSpacing*vectorSpacing];
for (x = 0; x != vectorSpacing; x++) {
vec[0] = x*(2.0/(vectorSpacing-1))-1;
for (y = 0; y != vectorSpacing; y++) {
vec[1] = y*(2.0/(vectorSpacing-1))-1;
for (z = 0; z != vectorSpacing; z++) {
vec[2] = z*(2.0/(vectorSpacing-1))-1;
drawVector(dd, vec);
}
}
}
} else {
int coord1 = (slice == SLICE_X) ? 1 : 0;
int coord2 = (slice == SLICE_Z) ? 1 : 2;
int slicecoord = slice-SLICE_X;
vec[slicecoord] = sliceval;
for (x = 0; x != vectorSpacing; x++) {
vec[coord1] = x*(2.0/(vectorSpacing-1))-1;
for (y = 0; y != vectorSpacing; y++) {
vec[coord2] = y*(2.0/(vectorSpacing-1))-1;
drawVector(dd, vec);
}
}
}
curfunc.render(g);
}
void genLines() {
if (vectors != null)
return;
partMult = fieldStrength = 10;
int i;
vecCount = 0;
int lineGridSize = lineDensityBar.getValue();
if (lineGridSize < 3)
lineGridSize = 3;
if (lineGridSize > 16)
lineGridSize = 16;
int slice = sliceChooser.getSelectedIndex();
boolean sliced = (slice > 0);
if (sliced)
lineGridSize *= 2;
int ct = (sliced) ? 30*lineGridSize*lineGridSize :
30*lineGridSize*lineGridSize*lineGridSize;
vectors = new FieldVector[ct];
double brightmult = 160*strengthBar.getValue();
boolean lineGrid[][][] =
new boolean[lineGridSize][lineGridSize][lineGridSize];
double lineGridMult = lineGridSize/2.;
if (sliced) {
int j, k;
int gp = (int) ((sliceval+1)*lineGridMult);
for (i = 0; i != lineGridSize; i++)
for (j = 0; j != lineGridSize; j++)
for (k = 0; k != lineGridSize; k++) {
switch (slice) {
case SLICE_X: lineGrid[i][j][k] = i!=gp; break;
case SLICE_Y: lineGrid[i][j][k] = j!=gp; break;
case SLICE_Z: lineGrid[i][j][k] = k!=gp; break;
}
}
}
double origp[] = new double[3];
double field[] = new double[3];
Particle p = new Particle();
p.lifetime = -1;
p.stepsize = 10;
int dir = -1;
int segs = 0;
double lastdist = 0;
for (i = 0; i != ct; i++) {
if (p.lifetime < 0) {
p.lifetime = 1;
p.stepsize = 10;
segs = 0;
lastdist = 0;
if (dir == 1) {
int j;
for (j = 0; j != 3; j++)
p.pos[j] = origp[j];
dir = -1;
continue;
}
dir = 1;
int px = 0, py = 0, pz = 0;
while (true) {
if (!lineGrid[px][py][pz])
break;
if (++px < lineGridSize)
continue;
px = 0;
if (++py < lineGridSize)
continue;
py = 0;
if (++pz < lineGridSize)
continue;
break;
}
if (pz == lineGridSize)
break;
lineGrid[px][py][pz] = true;
double offs = .5/lineGridMult;
origp[0] = p.pos[0] = px/lineGridMult-1+offs;
origp[1] = p.pos[1] = py/lineGridMult-1+offs;
origp[2] = p.pos[2] = pz/lineGridMult-1+offs;
if (sliced)
origp[slice-SLICE_X] = p.pos[slice-SLICE_X] = sliceval;
}
FieldVector fv = vectors[vecCount];
if (fv == null) {
fv = vectors[vecCount] = new FieldVector();
fv.p1 = new double[3];
fv.p2 = new double[3];
}
vecCount++;
fv.p1[0] = p.pos[0]; fv.p1[1] = p.pos[1]; fv.p1[2] = p.pos[2];
double x[] = p.pos;
lineSegment(p, dir);
//System.out.print(x[0] + " " + x[1] + " " + x[2] + "\n");
if (p.lifetime < 0) {
vecCount--;
continue;
}
int gx = (int) ((x[0]+1)*lineGridMult);
int gy = (int) ((x[1]+1)*lineGridMult);
int gz = (int) ((x[2]+1)*lineGridMult);
if (!lineGrid[gx][gy][gz])
segs--;
lineGrid[gx][gy][gz] = true;
fv.p2[0] = p.pos[0]; fv.p2[1] = p.pos[1]; fv.p2[2] = p.pos[2];
double dn = brightmult*p.phi;
if (dn > 2)
dn = 2;
fv.col = (int) (dn*255);
double d2 = dist2(origp, x);
if (d2 > lastdist)
lastdist = d2;
else
segs++;
if (segs > 10 || d2 < .001)
p.lifetime = -1;
}
//System.out.print("vc " + vecCount + " " + ct + "\n");
}
void drawLines(Graphics g) {
int i;
for (i = 0; i != vecCount; i++) {
FieldVector fv = vectors[i];
double x[] = fv.p1;
map3d(x[0], x[1], x[2], xpoints, ypoints, 0);
int vp1 = curfunc.getViewPri(cameraPos, x);
x = fv.p2;
map3d(x[0], x[1], x[2], xpoints, ypoints, 1);
fv.sx1 = xpoints[0];
fv.sy1 = ypoints[0];
fv.sx2 = xpoints[1];
fv.sy2 = ypoints[1];
int vp2 = curfunc.getViewPri(cameraPos, x);
fv.viewPri = (vp1 > vp2) ? vp1 : vp2;
}
curfunc.render(g);
}
#ifndef BUILD_M
double potfield[];
class EquipPoint {
double pos[];
double pot;
EquipPoint() { pos = new double[3]; }
EquipPoint(EquipPoint a, EquipPoint b) {
pos = new double[3];
int i;
for (i = 0; i != 3; i++)
pos[i] = .5*(a.pos[i] + b.pos[i]);
curfunc.getField(potfield, pos);
pot = reverse*potfield[0];
}
void set(int cx, int cy, int cz, double x, double y, double z) {
pos[cx] = x; pos[cy] = y; pos[cz] = z;
}
boolean valid() {
return !(Double.isNaN(pot) || Double.isInfinite(pot));
}
boolean inRange() {
return (pot >= -2 && pot <= 2);
}
void setPot(double p) { pot = p; }
}
boolean canSubdivide(EquipPoint a, EquipPoint b) {
return dist2(a.pos, b.pos) > .04*.04;
}
static final int maxVectors = 10000;
void genEquips() {
if (vectors != null)
return;
partMult = fieldStrength = 10;
vecCount = 0;
int slice = sliceChooser.getSelectedIndex();
vectors = new FieldVector[maxVectors];
potfield = new double[3];
EquipPoint eps[] = new EquipPoint[4];
int i;
for (i = 0; i != 4; i++)
eps[i] = new EquipPoint();
if (slice == SLICE_NONE) {
int steps = 3;
for (i = -steps; i <= steps; i++)
genEquipPlane(eps, i/(double) steps, SLICE_X);
for (i = -steps; i <= steps; i++)
genEquipPlane(eps, i/(double) steps, SLICE_Y);
for (i = -steps; i <= steps; i++)
genEquipPlane(eps, i/(double) steps, SLICE_Z);
} else
genEquipPlane(eps, sliceval, slice);
//System.out.print("vc " + vecCount + "\n");
}
void genEquipPlane(EquipPoint eps[], double z, int slice) {
int i, j;
int coord1 = (slice == SLICE_X) ? 1 : 0;
int coord2 = (slice == SLICE_Z) ? 1 : 2;
slice -= SLICE_X;
int grid = (sliceChooser.getSelectedIndex() == SLICE_NONE) ? 12 : 24;
double gridmult = 2./grid;
double pots[][] = new double[grid+1][grid+1];
for (i = 0; i <= grid; i++)
for (j = 0; j <= grid; j++) {
double x1 = i*gridmult-1;
double y1 = j*gridmult-1;
eps[0].set(coord1, coord2, slice, x1, y1, z);
curfunc.getField(potfield, eps[0].pos);
pots[i][j] = reverse*potfield[0];
}
for (i = 0; i != grid; i++)
for (j = 0; j != grid; j++) {
double x1 = i*gridmult-1;
double y1 = j*gridmult-1;
double x2 = (i+1)*gridmult-1;
double y2 = (j+1)*gridmult-1;
eps[0].set(coord1, coord2, slice, x1, y1, z);
eps[1].set(coord1, coord2, slice, x2, y1, z);
eps[2].set(coord1, coord2, slice, x1, y2, z);
eps[3].set(coord1, coord2, slice, x2, y2, z);
eps[0].setPot(pots[i ][j ]);
eps[1].setPot(pots[i+1][j ]);
eps[2].setPot(pots[i ][j+1]);
eps[3].setPot(pots[i+1][j+1]);
tryEdges(eps[0], eps[1], eps[2], eps[3]);
}
}
double max(double a, double b) { return a > b ? a : b; }
double min(double a, double b) { return a < b ? a : b; }
boolean shouldSubdivide(EquipPoint ep1, EquipPoint ep2, EquipPoint ep3,
EquipPoint ep4) {
if (!ep1.inRange())
return true;
if (!ep2.inRange())
return true;
if (!ep3.inRange())
return true;
if (!ep4.inRange())
return true;
double pmin = min(min(ep1.pot, ep2.pot), min(ep3.pot, ep4.pot));
double pmax = max(max(ep1.pot, ep2.pot), max(ep3.pot, ep4.pot));
return (pmax-pmin) > .3;
}
void tryEdges(EquipPoint ep1, EquipPoint ep2, EquipPoint ep3,
EquipPoint ep4) {
if (shouldSubdivide(ep1, ep2, ep3, ep4) && canSubdivide(ep1, ep2)) {
EquipPoint ep12 = new EquipPoint(ep1, ep2);
EquipPoint ep13 = new EquipPoint(ep1, ep3);
EquipPoint ep24 = new EquipPoint(ep2, ep4);
EquipPoint ep34 = new EquipPoint(ep3, ep4);
EquipPoint epc = new EquipPoint(ep12, ep34);
tryEdges(ep1, ep12, ep13, epc);
tryEdges(ep12, ep2, epc, ep24);
tryEdges(ep13, epc, ep3, ep34);
tryEdges(epc, ep24, ep34, ep4);
return;
}
tryEdge(ep1, ep2, ep3, ep4);
tryEdge(ep1, ep2, ep1, ep3);
tryEdge(ep1, ep2, ep2, ep4);
tryEdge(ep1, ep3, ep2, ep4);
tryEdge(ep1, ep3, ep3, ep4);
tryEdge(ep2, ep4, ep3, ep4);
}
boolean spanning(EquipPoint ep1, EquipPoint ep2, double pval) {
if (ep1.pot == ep2.pot)
return false;
if (!(ep1.valid() && ep2.valid()))
return false;
return !((ep1.pot < pval && ep2.pot < pval) ||
(ep1.pot > pval && ep2.pot > pval));
}
void interpPoint(EquipPoint ep1, EquipPoint ep2, double pval,
double pos[]) {
double interp2 = (pval-ep1.pot)/(ep2.pot-ep1.pot);
double interp1 = 1-interp2;
//System.out.print("I " + interp1 + " " + interp2 + "\n");
//System.out.print(ep1.pot + " " + ep2.pot + " " + Double.isNaN(ep1.pot) + "\n");
int i;
for (i = 0; i != 3; i++)
pos[i] = ep1.pos[i]*interp1+ep2.pos[i]*interp2;
//System.out.print("> " + pos[0] + " " + pos[1] + " " + pos[2] + "\n");
}
void tryEdge(EquipPoint ep1, EquipPoint ep2,
EquipPoint ep3, EquipPoint ep4) {
int i;
if (sliceChooser.getSelectedIndex() == SLICE_NONE) {
tryEdge(ep1, ep2, ep3, ep4, (potentialBar.getValue()-500)/500.);
} else {
for (i = -20; i <= 20; i++)
tryEdge(ep1, ep2, ep3, ep4, i/20.);
}
}
void tryEdge(EquipPoint ep1, EquipPoint ep2,
EquipPoint ep3, EquipPoint ep4, double pval) {
if (!(spanning(ep1, ep2, pval) && spanning(ep3, ep4, pval)))
return;
if (vecCount == maxVectors)
return;
FieldVector fv = vectors[vecCount];
if (fv == null) {
fv = vectors[vecCount] = new FieldVector();
fv.p1 = new double[3];
fv.p2 = new double[3];
}
vecCount++;
interpPoint(ep1, ep2, pval, fv.p1);
interpPoint(ep3, ep4, pval, fv.p2);
fv.col = 255+((int) (255*pval));
}
#endif /* BUILD_M */
// draw the appropriate field vector at xx,yy,zz
void drawVector(DrawData dd, double vec[]) {
double field[] = dd.field;
// calculate field vector
curfunc.getField(field, vec);
double dn = java.lang.Math.sqrt(field[0]*field[0]+field[1]*field[1]+
field[2]*field[2]);
double dnr = dn*reverse;
if (dn > 0) {
field[0] /= dnr;
field[1] /= dnr;
field[2] /= dnr;
}
dn *= dd.mult;
if (dn > 2)
dn = 2;
int col = (int) (dn*255);
double sw2 = 1./(vectorSpacing-1);
map3d(vec[0], vec[1], vec[2], xpoints, ypoints, 0);
double vv[] = dd.vv;
vv[0] = vec[0] + sw2*field[0];
vv[1] = vec[1] + sw2*field[1];
vv[2] = vec[2] + sw2*field[2];
map3d(vv[0], vv[1], vv[2], xpoints, ypoints, 1);
FieldVector fv = vectors[vecCount];
if (fv == null)
fv = vectors[vecCount] = new FieldVector();
fv.sx1 = xpoints[0];
fv.sy1 = ypoints[0];
fv.sx2 = xpoints[1];
fv.sy2 = ypoints[1];
fv.col = col;
vecCount++;
int vp1 = curfunc.getViewPri(cameraPos, vec);
if (!curfunc.noSplitFieldVectors())
fv.viewPri = vp1;
else {
int vp2 = curfunc.getViewPri(cameraPos, vv);
fv.viewPri = (vp1 == vp2) ? vp1 : -1;
}
}
void drawParticles(Graphics g) {
int i;
int pcount = getParticleCount();
for (i = 0; i < pcount; i++) {
Particle pt = particles[i];
pt.viewPri = curfunc.getViewPri(cameraPos, pt.pos);
}
curfunc.render(g);
}
void moveParticles() {
fieldStrength = strengthBar.getValue();
int bestd = 0;
int i;
int pcount = getParticleCount();
int slice = sliceChooser.getSelectedIndex();
boolean sliced = (slice > 0);
partMult = fieldStrength * reverse * timeStep;
for (i = 0; i != pcount; i++) {
Particle pt = particles[i];
removeFromDensityGroup(pt);
moveParticle(pt);
double x[] = pt.pos;
if (!(x[0] >= -1 && x[0] < 1 &&
x[1] >= -1 && x[1] < 1 &&
x[2] >= -1 && x[2] < 1) ||
(pt.lifetime -= timeStep) < 0) {
positionParticle(pt);
}
if (sliced)
x[slice-SLICE_X] = sliceval;
int d = addToDensityGroup(pt);
if (d > bestd)
bestd = d;
}
boolean withforce =
(dispChooser.getSelectedIndex() == DISP_PART_FORCE);
int maxd = (10*getParticleCount()/(densitygridsize*densitygridsize*
densitygridsize));
if (sliced)
maxd = 4*getParticleCount()/(densitygridsize*densitygridsize);
if (!withforce && curfunc.redistribute() && bestd > maxd)
redistribute(bestd);
}
static int frames = 0;
static int framerate = 0;
static long firsttime = 0;
// draw the cube containing the particles. if drawAll is false then
// we just draw faces that are facing the camera. This routine draws
// each edge twice which is unnecessary, but easier.
void drawCube(Graphics g, boolean drawAll) {
int i;
int slice = sliceChooser.getSelectedIndex();
int sp = (drawAll) ? 0 : 8;
for (i = 0; i != 6; i++) {
// calculate normal of ith face
int nx = (i == 0) ? -1 : (i == 1) ? 1 : 0;
int ny = (i == 2) ? -1 : (i == 3) ? 1 : 0;
int nz = (i == 4) ? -1 : (i == 5) ? 1 : 0;
// if face is not facing camera, don't draw it
if (!drawAll && backFacing(nx, ny, nz, nx, ny, nz))
continue;
double pts[];
pts = new double[3];
int n;
for (n = 0; n != 4; n++) {
computeFace(i, n, pts);
map3d(pts[0], pts[1], pts[2], xpoints, ypoints, n);
}
g.setColor(Color.gray);
g.drawPolygon(xpoints, ypoints, 4);
if (slice != SLICE_NONE && i/2 != slice-SLICE_X) {
if (selectedSlice)
g.setColor(Color.yellow);
int coord1 = (slice == SLICE_X) ? 1 : 0;
int coord2 = (slice == SLICE_Z) ? 1 : 2;
computeFace(i, 0, pts);
pts[slice-SLICE_X] = sliceval;
map3d(pts[0], pts[1], pts[2],
slicerPoints[0], slicerPoints[1], sp);
computeFace(i, 2, pts);
pts[slice-SLICE_X] = sliceval;
map3d(pts[0], pts[1], pts[2],
slicerPoints[0], slicerPoints[1], sp+1);
g.drawLine(slicerPoints[0][sp ], slicerPoints[1][sp],
slicerPoints[0][sp+1], slicerPoints[1][sp+1]);
if (drawAll) {
// we don't keep track of the sliceFaces and slicerPoints
// unless drawAll is true, because if it's false then
// we're not drawing all the faces of the cube and the
// numbering will get screwed up. So if drawAll is
// false we just point sp to scratch space and don't
// bother storing the sliceFaces.
sliceFaces[sp/2][0] = nx;
sliceFaces[sp/2][1] = ny;
sliceFaces[sp/2][2] = nz;
sp += 2;
}
}
}
}
// generate the nth vertex of the bth cube face
void computeFace(int b, int n, double pts[]) {
// One of the 3 coordinates (determined by a) is constant.
// When b=0, x=-1; b=1, x=+1; b=2, y=-1; b=3, y=+1; etc
int a = b >> 1;
pts[a] = ((b & 1) == 0) ? -1 : 1;
// fill in the other 2 coordinates with one of the following
// (depending on n): -1,-1; +1,-1; +1,+1; -1,+1
int i;
for (i = 0; i != 3; i++) {
if (i == a) continue;
pts[i] = (((n>>1)^(n&1)) == 0) ? -1 : 1;
n >>= 1;
}
}
void renderItems(Graphics g, int pri) {
g.setColor(Color.white);
int disp = dispChooser.getSelectedIndex();
if (disp == DISP_VECTORS || disp == DISP_VECTORS_A) {
int i;
for (i = 0; i != vecCount; i++) {
FieldVector fv = vectors[i];
if (fv.viewPri != pri)
continue;
g.setColor(fieldColors[fv.col]);
drawArrow(g, null, fv.sx1, fv.sy1, fv.sx2, fv.sy2, 2);
}
return;
}
if (disp == DISP_LINES || disp == DISP_EQUIPS) {
int i;
g.setColor(Color.white);
Color colvec[] = (disp == DISP_EQUIPS) ? equipColors : fieldColors;
for (i = 0; i != vecCount; i++) {
FieldVector fv = vectors[i];
if (fv.viewPri != pri)
continue;
if (fv.sx1 == fv.sx2 && fv.sy1 == fv.sy2)
continue;
g.setColor(colvec[fv.col]);
g.drawLine(fv.sx1, fv.sy1, fv.sx2, fv.sy2);
}
return;
}
int pcount = getParticleCount();
int i;
wooft += .3;
for (i = 0; i < pcount; i++) {
Particle p = particles[i];
if (p.viewPri != pri)
continue;
double pos[] = p.pos;
map3d(pos[0], pos[1], pos[2], xpoints, ypoints, 0);
if (xpoints[0] < 0 || xpoints[0] >= winSize.width ||
ypoints[0] < 0 || ypoints[0] >= winSize.height)
continue;
if (disp == DISP_PART_MAG) {
double cosph = java.lang.Math.cos(p.phi);
double sinph = java.lang.Math.sin(p.phi);
double costh = java.lang.Math.cos(p.theta);
double sinth = java.lang.Math.sin(p.theta);
double al = .08;
double rhatx = sinth*cosph*al;
double rhaty = sinth*sinph*al;
double rhatz = costh*al;
map3d(pos[0]+rhatx, pos[1]+rhaty, pos[2]+rhatz,
xpoints, ypoints, 1);
drawArrow(g, null, xpoints[0], ypoints[0],
xpoints[1], ypoints[1], 2);
} else
g.fillRect(xpoints[0], ypoints[0]-1, 2, 2);
}
}
double wooft = 0;
void drawPlane(Graphics g, double sizex, double sizey, double z) {
g.setColor(darkYellow);
map3d(-sizex, -sizey, z, xpoints, ypoints, 0);
map3d(+sizex, -sizey, z, xpoints, ypoints, 1);
map3d(+sizex, +sizey, z, xpoints, ypoints, 2);
map3d(-sizex, +sizey, z, xpoints, ypoints, 3);
g.fillPolygon(xpoints, ypoints, 4);
}
void drawArrow(Graphics g, String text, int x1, int y1, int x2, int y2) {
drawArrow(g, text, x1, y1, x2, y2, 5);
}
void drawArrow(Graphics g, String text,
int x1, int y1, int x2, int y2, int as) {
g.drawLine(x1, y1, x2, y2);
double l = java.lang.Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
if (l > as/2) { // was as
double hatx = (x2-x1)/l;
double haty = (y2-y1)/l;
g.drawLine(x2, y2,
(int) (haty*as-hatx*as+x2),
(int) (-hatx*as-haty*as+y2));
g.drawLine(x2, y2,
(int) (-haty*as-hatx*as+x2),
(int) (hatx*as-haty*as+y2));
if (text != null)
g.drawString(text, (int) (x2+hatx*10), (int) (y2+haty*10));
}
}
boolean backFacing(double px, double py, double pz,
double nx, double ny, double nz) {
double x = cameraPos[0]-px;
double y = cameraPos[1]-py;
double z = cameraPos[2]-pz;
double d = x*nx+y*ny+z*nz;
return d <= 0;
}
int intersectSphere(double cp[], double ptx, double pty, double ptz,
double r) {
return intersectSphere(cp, ptx, pty, ptz, 0, 0, 0, r);
}
// calculate intersection of a ray starting at the camera and going
// through point (ptx,pty,ptz) with a sphere of radius 2 at the origin.
// Returns 0 if there is no intersection before the ray hits the point,
// 1 if the point is inside the sphere, and 2 if the point is behind
// the sphere.
int intersectSphere(double cp[], double ptx, double pty, double ptz,
double sx, double sy, double sz, double r) {
double vx = ptx-cp[0];
double vy = pty-cp[1];
double vz = ptz-cp[2];
double qpx = cp[0]-sx;
double qpy = cp[1]-sy;
double qpz = cp[2]-sz;
double a = vx*vx+vy*vy+vz*vz;
double b = 2*(vx*qpx + vy*qpy + vz*qpz);
double c = qpx*qpx + qpy*qpy + qpz*qpz - r*r;
double discrim = b*b-4*a*c;
if (discrim < 0)
return 0;
discrim = java.lang.Math.sqrt(discrim);
double b1 = (-b-discrim)/(2*a);
double b2 = (-b+discrim)/(2*a);
if (b1 < 1 && inViewBox(b1, cp, vx, vy, vz))
return (b2 < 1) ? 2 : 1;
else
return 0;
}
// calculate intersection of a ray starting at the camera and going
// through point (ptx,pty,ptz) with the plane z=a. Returns 0 if there
// is no intersection before the ray hits the point; otherwise 2.
double intersectZPlane(double cp[], double a,
double ptx, double pty, double ptz) {
double vx = ptx-cp[0];
double vy = pty-cp[1];
double vz = ptz-cp[2];
double t = intersectionDistance = -(cp[2]+a)/vz;
if (t > 1)
return 0;
if (!inViewBox(t, cp, vx, vy, vz))
return 0;
return 2;
}
// make sure the intersection is inside the viewing box
boolean inViewBox(double t, double cp[], double vx, double vy, double vz) {
if (t < 0)
return false;
double ix = intersection[0] = cp[0]+vx*t;
double iy = intersection[1] = cp[1]+vy*t;
double iz = intersection[2] = cp[2]+vz*t;
//System.out.print("ivb " + t + " " + ix + " " + iy + " " + iz + "\n");
if (ix < -1 || ix > 1 || iy < -1 || iy > 1 ||
iz < -1 || iz > 1)
return false;
return true;
}
int intersectCylinder(double cp[], double ptx, double pty, double ptz,
double r, boolean vbTest) {
return intersectCylinder(cp, ptx, pty, ptz, 0, 0, r, vbTest);
}
// calculate intersection of a ray starting at the camera and
// going through point (ptx,pty,ptz) with a cylinder of radius r
// centered at the z axis. Returns 0 if there is no intersection
// before the ray hits the point, 1 if the point is inside the
// cylinder, and 2 if the point is behind the cylinder.
int intersectCylinder(double cp[], double ptx, double pty, double ptz,
double sx, double sy, double r, boolean vbTest) {
double vx = ptx-cp[0];
double vy = pty-cp[1];
double qpx = cp[0]-sx;
double qpy = cp[1]-sy;
double a = vx*vx+vy*vy;
double b = 2*(vx*qpx + vy*qpy);
double c = qpx*qpx + qpy*qpy - r*r;
double discrim = b*b-4*a*c;
if (discrim < 0)
return 0;
discrim = java.lang.Math.sqrt(discrim);
double b1 = (-b-discrim)/(2*a);
double b2 = (-b+discrim)/(2*a);
//System.out.print(b1 + " " +b2 + "\n");
// test if first intersection is behind point
if (b1 > 1)
return 0;
// if vbTest is true, check if intersection is inside box
if (!vbTest || inViewBox(b1, cp, vx, vy, ptz-cp[2]))
// if the second intersection is between camera and point,
// the point is behind the cylinder. Otherwise it's inside.
return (b2 < 1) ? 2 : 1;
// If the second intersection is between camera and point,
// the point is inside the cylinder.
if (b2 > 1)
return 2;
// If the second intersection is in the view box, the point
// is behind the cylinder.
if (inViewBox(b2, cp, vx, vy, ptz-cp[2]))
return 2;
// otherwise there is no valid intersection.
return 0;
}
int rediscount;
void redistribute(int mostd) {
if (mostd < 5)
return;
rediscount++;
int maxd = (10*getParticleCount()/
(densitygridsize*densitygridsize*densitygridsize));
int i;
int pn = 0;
int pcount = getParticleCount();
for (i = rediscount % 4; i < pcount; i+=4) {
Particle p = particles[i];
int a = (int)((p.pos[0]+1)*(densitygridsize/2));
int b = (int)((p.pos[1]+1)*(densitygridsize/2));
int c = (int)((p.pos[2]+1)*(densitygridsize/2));
if (density[a][b][c] <= maxd)
continue;
p.lifetime = -1;
pn++;
}
//System.out.print("redist " + mostd + " " + pn + "\n");
}
double distance(Particle p) {
return distance(p.pos[0], p.pos[1], p.pos[2]);
}
double distance(double y[]) {
return distance(y[0], y[1], y[2]);
}
double distance(double x, double y, double z) {
return java.lang.Math.sqrt(x*x+y*y+z*z+.000000001);
}
double distance(double x, double y) {
return java.lang.Math.sqrt(x*x+y*y+.000000001);
}
void rotateParticleAdd(double result[], double y[], double mult,
double cx, double cy) {
result[0] += -mult*(y[1]-cy);
result[1] += mult*(y[0]-cx);
result[2] += 0;
}
void rotateParticle(double result[], double y[], double mult) {
result[0] = -mult*y[1];
result[1] = mult*y[0];
result[2] = 0;
}
public void componentHidden(ComponentEvent e){}
public void componentMoved(ComponentEvent e){}
public void componentShown(ComponentEvent e) {
cv.repaint(pause);
}
public void componentResized(ComponentEvent e) {
handleResize();
cv.repaint(pause);
}
public void actionPerformed(ActionEvent e) {
vectors = null;
if (e.getSource() == resetButton)
resetParticles();
if (e.getSource() == kickButton)
kickParticles();
if (e.getSource() == infoButton) {
String s = curfunc.getClass().getName();
try {
s = s.substring(s.lastIndexOf('.')+1);
applet.getAppletContext().showDocument(
new URL(applet.getCodeBase(),
"functions.html" + '#' + s),
"functionHelp");
} catch (Exception ex) {
}
}
curfunc.actionPerformed();
}
public boolean handleEvent(Event ev) {
if (ev.id == Event.WINDOW_DESTROY) {
destroyFrame();
return true;
}
return super.handleEvent(ev);
}
void destroyFrame() {
if (applet == null)
dispose();
else
applet.destroyFrame();
}
public void adjustmentValueChanged(AdjustmentEvent e) {
vectors = null;
System.out.print(((Scrollbar) e.getSource()).getValue() + "\n");
if (e.getSource() == partCountBar)
resetDensityGroups();
cv.repaint(pause);
}
public void mouseDragged(MouseEvent e) {
dragging = true;
oldDragX = dragX;
oldDragY = dragY;
dragX = e.getX(); dragY = e.getY();
int mode = modeChooser.getSelectedIndex();
if (selectedSlice)
mode = MODE_SLICE;
if (mode == MODE_ANGLE) {
int xo = oldDragX-dragX;
int yo = oldDragY-dragY;
rotate(lastXRot = xo/40., lastYRot = -yo/40.);
double lr = Math.sqrt(lastXRot*lastXRot + lastYRot*lastYRot);
if (lr > .06) {
lr /= .06;
lastXRot /= lr;
lastYRot /= lr;
}
cv.repaint(pause);
} else if (mode == MODE_ZOOM) {
int xo = dragX-dragStartX;
zoom = dragZoomStart + xo/20.;
if (zoom < .1)
zoom = .1;
cv.repaint(pause);
} else if (mode == MODE_SLICE) {
double x3[] = new double[3];
unmap3d(x3, dragX, dragY, sliceFace, sliceFace, viewMain);
switch (sliceChooser.getSelectedIndex()) {
case SLICE_X: sliceval = x3[0]; break;
case SLICE_Y: sliceval = x3[1]; break;
case SLICE_Z: sliceval = x3[2]; break;
}
// Avoid -1 because it causes particles to drift out of the
// cube too easily. Avoid +1 because of that and because it is
// not included in any density group.
if (sliceval < -.99)
sliceval = -.99;
if (sliceval > .99)
sliceval = .99;
resetDensityGroups();
//System.out.print(sliceval + "\n");
cv.repaint(pause);
vectors = null;
}
}
boolean csInRange(int x, int xa, int xb) {
if (xa < xb)
return x >= xa-5 && x <= xb+5;
return x >= xb-5 && x <= xa+5;
}
void checkSlice(int x, int y) {
if (sliceChooser.getSelectedIndex() == SLICE_NONE) {
selectedSlice = false;
return;
}
int n;
selectedSlice = false;
for (n = 0; n != 8; n += 2) {
int xa = slicerPoints[0][n];
int xb = slicerPoints[0][n+1];
int ya = slicerPoints[1][n];
int yb = slicerPoints[1][n+1];
if (!csInRange(x, xa, xb) || !csInRange(y, ya, yb))
continue;
double d;
if (xa == xb)
d = java.lang.Math.abs(x-xa);
else {
// write line as y=a+bx
double b = (yb-ya)/(double) (xb-xa);
double a = ya-b*xa;
// solve for distance
double d1 = y-(a+b*x);
if (d1 < 0)
d1 = -d1;
d = d1/java.lang.Math.sqrt(1+b*b);
}
if (d < 6) {
selectedSlice = true;
sliceFace = sliceFaces[n/2];
break;
}
}
}
public void mouseMoved(MouseEvent e) {
dragX = e.getX(); dragY = e.getY();
dragStartX = dragX;
dragStartY = dragY;
dragZoomStart = zoom;
boolean ss = selectedSlice;
checkSlice(dragX, dragY);
if (ss != selectedSlice)
cv.repaint(pause);
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
mouseDown = true;
}
public void mouseReleased(MouseEvent e) {
mouseDown = false;
}
void dispChooserChanged() {
int disp = dispChooser.getSelectedIndex();
showA = (disp == DISP_PART_VELOC_A || disp == DISP_VECTORS_A);
getPot = (disp == DISP_EQUIPS);
if (disp == DISP_PART_FORCE)
kickButton.enable();
else
kickButton.disable();
potentialLabel.hide();
potentialBar.hide();
vecDensityLabel.hide();
vecDensityBar.hide();
lineDensityLabel.hide();
lineDensityBar.hide();
partCountLabel.hide();
partCountBar.hide();
strengthLabel.show();
strengthBar.show();
if (disp == DISP_VECTORS || disp == DISP_VECTORS_A ||
disp == DISP_VIEW_PAPER) {
vecDensityLabel.show();
vecDensityBar.show();
} else if (disp == DISP_LINES) {
lineDensityLabel.show();
lineDensityBar.show();
} else if (disp == DISP_EQUIPS) {
potentialLabel.show();
potentialBar.show();
} else {
partCountLabel.show();
partCountBar.show();
}
vecDensityLabel.setText(disp == DISP_VIEW_PAPER ?
"Resolution" : "Vector Density");
if (disp == DISP_EQUIPS) {
strengthLabel.hide();
strengthBar.hide();
}
if ((disp == DISP_VIEW_PAPER || disp == DISP_EQUIPS) &&
sliceChooser.getSelectedIndex() == SLICE_NONE) {
sliceChooser.select(curfunc.getBestSlice());
potentialBar.disable();
}
validate();
resetParticles();
}
public void itemStateChanged(ItemEvent e) {
vectors = null;
cv.repaint(pause);
reverse = (reverseCheck.getState()) ? -1 : 1;
if (e.getItemSelectable() == dispChooser) {
dispChooserChanged();
resetParticles();
}
if (e.getItemSelectable() == sliceChooser) {
resetParticles();
if (modeChooser.getSelectedIndex() == MODE_SLICE)
modeChooser.select(MODE_ANGLE);
if (sliceChooser.getSelectedIndex() == SLICE_NONE)
potentialBar.enable();
else
potentialBar.disable();
}
if (e.getStateChange() != ItemEvent.SELECTED)
return;
if (e.getItemSelectable() == functionChooser)
functionChanged();
}
void functionChanged() {
reverse = 1;
reverseCheck.setState(false);
parseError = false;
curfunc = (VecFunction)
functionList.elementAt(functionChooser.getSelectedIndex());
int i;
for (i = 0; i != 3; i++) {
auxBars[i].label.hide();
auxBars[i].bar.hide();
textFields[i].hide();
if (textFieldLabel != null)
textFieldLabel.hide();
}
strengthBar.setValue(20);
int x = dispChooser.getSelectedIndex();
setupDispChooser(!curfunc.nonGradient());
try {
dispChooser.select(x);
} catch (Exception e) { }
curfunc.setup();
sliceChooser.select(SLICE_NONE);
validate();
resetParticles();
dispChooserChanged();
}
void setupDispChooser(boolean potential) {
dispChooser.removeAll();
dispChooser.add("Display: Particles (Vel.)");
#ifdef BUILD_M
dispChooser.add("Display: Parts (A Field, Vel.)");
dispChooser.add("Display: Field Vectors");
dispChooser.add("Display: Field Vectors (A)");
#else
dispChooser.add("Display: Particles (Force)");
dispChooser.add("Display: Field Vectors");
#endif
#ifdef BUILD_V
dispChooser.add("Display: Streamlines");
#else
dispChooser.add("Display: Field Lines");
#endif
#ifdef BUILD_M
dispChooser.add("Display: Parts (Magnetic)");
dispChooser.add("Display: Mag View Film");
#else
if (potential) {
dispChooser.add("Display: Equipotentials");
}
#endif
}
void setupBar(int n, String text, int val) {
auxBars[n].label.setText(text);
auxBars[n].label.show();
auxBars[n].bar.setValue(val);
auxBars[n].bar.show();
}
boolean useMagnetMove() {
int disp = dispChooser.getSelectedIndex();
return (disp == DISP_PART_MAG);
}
#ifdef BUILD_M
MagnetState mstates[];
void magneticMoveParticle(Particle p) {
int i;
if (mstates == null) {
mstates = new MagnetState[3];
for (i = 0; i != 3; i++)
mstates[i] = new MagnetState();
}
MagnetState ms = mstates[0];
MagnetState mshalf = mstates[1];
MagnetState oldms = mstates[2];
for (i = 0; i != 3; i++) {
ms.pos[i] = p.pos[i];
ms.vel[i] = p.vel[i];
ms.theta = p.theta;
ms.thetav = p.thetav;
ms.phi = p.phi;
ms.phiv = p.phiv;
}
mshalf.copy(ms);
oldms.copy(ms);
double h = 1;
final double minh = .01;
final double maxh = 1;
final double E = .1;
int steps = 0;
boolean adapt = curfunc.useAdaptiveRungeKutta() &&
curfunc.useRungeKutta();
boundCheck = false;
double t = 0;
while (t < 1) {
// estimate one full step
magnetMove(ms, h);
if (boundCheck) {
p.pos[0] = -100;
return;
}
if (curfunc.checkBounds(ms.pos, oldms.pos)) {
p.pos[0] = -100;
return;
}
if (!adapt)
break;
// estimate two half steps
magnetMove(mshalf, h*.5);
magnetMove(mshalf, h*.5);
// estimate the local error
double localError =
java.lang.Math.abs(ms.pos[0] - mshalf.pos[0]) +
java.lang.Math.abs(ms.pos[1] - mshalf.pos[1]) +
java.lang.Math.abs(ms.pos[2] - mshalf.pos[2]) +
java.lang.Math.abs(ms.theta - mshalf.theta) +
java.lang.Math.abs(ms.phi - mshalf.phi);
if (localError > E && h > minh) {
h *= 0.75; // decrease the step size
if (h < minh)
h = minh;
ms.copy(oldms);
continue;
} else if (localError < (E * 0.5)) {
h *= 1.25; // increase the step size
if (h > maxh)
h = maxh;
}
mshalf.copy(ms);
t += h;
steps++;
}
/*if (steps > 1)
System.out.print(steps + "\n");*/
for (i = 0; i != 3; i++) {
p.pos[i] = ms.pos[i];
p.vel[i] = ms.vel[i];
p.theta = ms.theta;
p.thetav = ms.thetav;
p.phi = ms.phi;
p.phiv = ms.phiv;
}
}
void magnetMove(MagnetState ms, double stepsize) {
double cosph = java.lang.Math.cos(ms.phi);
double sinph = java.lang.Math.sin(ms.phi);
double costh = java.lang.Math.cos(ms.theta);
double sinth = java.lang.Math.sin(ms.theta);
// rhat = (sinth*cosph, sinth*sinph, costh)
// thhat = (costh*cosph, costh*sinph, -sinth)
// phhat = (-sinph, cosph, 0)
// These three vectors are always perpendicular and rhat X
// thhat = phhat. The particle's arrow points in the
// direction of rhat, so the points p.pos + thhat, p.pos +
// phhat, p.pos - tthat, p.pos - phhat are the four points we
// sample on the current loop (if we pretend that each
// particle has a current loop which goes around the arrow)
double thhat[] = new double[3];
double phhat[] = new double[3];
double thhatn[] = new double[3];
double phhatn[] = new double[3];
double force[] = new double[3];
double torque[] = new double[3];
thhat[0] = costh*cosph; thhat[1] = costh*sinph; thhat[2] = -sinth;
phhat[0] = -sinph; phhat[1] = cosph; phhat[2] = 0;
int i;
for (i = 0; i != 3; i++) {
thhatn[i] = -thhat[i];
phhatn[i] = -phhat[i];
force[i] = torque[i] = 0;
}
getMagForce(ms.pos, thhat, phhat, force, torque);
getMagForce(ms.pos, phhat, thhatn, force, torque);
getMagForce(ms.pos, thhatn, phhatn, force, torque);
getMagForce(ms.pos, phhatn, thhat, force, torque);
for (i = 0; i != 3; i++) {
ms.vel[i] += force[i]*stepsize;
ms.pos[i] += ms.vel[i]*stepsize;
}
// turning about the phhat axis causes theta to change.
ms.thetav += dot(torque, phhat)*1000*stepsize;
// turning about the z axis causes phi to change.
ms.phiv += torque[2]*1000*stepsize; // torque . zhat
// heavily damp the oscillation of the magnets
ms.thetav *= java.lang.Math.exp(-.2*stepsize);
ms.phiv *= java.lang.Math.exp(-.2*stepsize);
ms.theta += ms.thetav*stepsize;
ms.phi += ms.phiv*stepsize;
}
void getMagForce(double pos[], double off[], double j[], double f[],
double torque[]) {
int i;
double offs[] = new double[3];
for (i = 0; i != 3; i++) {
offs[i] = off[i] * .02;
rk_yn[i] = pos[i] + offs[i];
}
curfunc.getField(rk_k1, rk_yn);
double fmult = reverse * strengthBar.getValue();
for (i = 0; i != 3; i++)
rk_k1[i] *= fmult;
double newf[] = new double[3];
double newtorque[] = new double[3];
cross(newf, j, rk_k1);
cross(newtorque, offs, newf);
for (i = 0; i != 3; i++) {
f[i] += newf[i];
torque[i] += newtorque[i];
}
}
class MagnetState {
MagnetState() { pos = new double[3]; vel = new double[3]; }
double pos[], vel[], theta, phi, thetav, phiv;
void copy(MagnetState ms) {
int i;
for (i = 0; i != 3; i++) {
pos[i] = ms.pos[i];
vel[i] = ms.vel[i];
theta = ms.theta;
thetav = ms.thetav;
phi = ms.phi;
phiv = ms.phiv;
}
}
};
#endif
void cross(double res[], double v1[], double v2[]) {
res[0] = v1[1]*v2[2] - v1[2]*v2[1];
res[1] = v1[2]*v2[0] - v1[0]*v2[2];
res[2] = v1[0]*v2[1] - v1[1]*v2[0];
}
double dot(double v1[], double v2[]) {
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
}
boolean boundCheck;
double oldY[];
double rk_k1[] = new double[6];
double rk_k2[] = new double[6];
double rk_k3[] = new double[6];
double rk_k4[] = new double[6];
double rk_yn[] = new double[6];
void rk(int order, double x, double Y[], double stepsize) {
int i;
// x is not used...
if (order == 3) {
// velocity-based motion
double fmult = stepsize * partMult;
for (i = 0; i != order; i++)
rk_yn[i] = Y[i];
curfunc.getField(rk_k1, rk_yn);
for (i = 0; i != order; i++)
rk_yn[i] = (Y[i] + 0.5*fmult*rk_k1[i]);
curfunc.getField(rk_k2, rk_yn);
for (i = 0; i != order; i++)
rk_yn[i] = (Y[i] + 0.5*fmult*rk_k2[i]);
curfunc.getField(rk_k3, rk_yn);
for (i = 0; i != order; i++)
rk_yn[i] = (Y[i] + fmult*rk_k3[i]);
curfunc.getField(rk_k4, rk_yn);
for (i = 0; i != order; i++)
Y[i] = Y[i] + fmult*(rk_k1[i]+2*(rk_k2[i]+rk_k3[i])+rk_k4[i])/6;
} else {
// force-based (could share more code with above, but this is
// called a lot so we want it to be fast)
double fmult = stepsize * partMult;
for (i = 0; i != order; i++)
rk_yn[i] = Y[i];
getForceField(rk_k1, rk_yn, stepsize, fmult);
for (i = 0; i != order; i++)
rk_yn[i] = (Y[i] + 0.5*rk_k1[i]);
getForceField(rk_k2, rk_yn, stepsize, fmult);
for (i = 0; i != order; i++)
rk_yn[i] = (Y[i] + 0.5*rk_k2[i]);
getForceField(rk_k3, rk_yn, stepsize, fmult);
for (i = 0; i != order; i++)
rk_yn[i] = (Y[i] + rk_k3[i]);
getForceField(rk_k4, rk_yn, stepsize, fmult);
for (i = 0; i != order; i++)
Y[i] = Y[i] + (rk_k1[i]+2*(rk_k2[i]+rk_k3[i])+rk_k4[i])/6;
}
}
void getForceField(double result[], double y[],
double stepsize, double fmult) {
// get the field vector for the current function
curfunc.getField(result, y);
// the field data has been written into result[0:2] where it will
// directly influence the position (y[0:2]), but we want it to
// influence the velocity (y[3:5]), so we move it. In the
// process, we multiply by fmult (to get the reverse button
// and field strength slider to work). It would be nice if we
// could just say getField(result+3, y), but that's java for you.
int i;
for (i = 0; i != 3; i++)
result[i+3] = .1*fmult*result[i];
// here we fill in result[0:2] so that the particle position will
// change according to the velocity.
for (i = 0; i != 3; i++)
result[i] = stepsize*timeStep*rk_yn[i+3];
}
double rk_Y[] = new double[6];
double rk_Yhalf[] = new double[6];
double rk_oldY[] = new double[6];
double ls_fieldavg[] = new double[3];
void moveParticle(Particle p)
{
int disp = dispChooser.getSelectedIndex();
#ifdef BUILD_M
if (disp == DISP_PART_MAG) {
magneticMoveParticle(p);
return;
}
#endif
int numIter=0;
double maxh=1;
double error=0.0, E = .001, localError;
boolean useForce = (disp == DISP_PART_FORCE);
int order = useForce ? 6 : 3;
double Y[] = rk_Y;
double Yhalf[] = rk_Yhalf;
oldY = rk_oldY;
int i;
for (i = 0; i != 3; i++)
oldY[i] = Y[i] = Yhalf[i] = p.pos[i];
if (useForce)
for (i = 0; i != 3; i++)
Y[i+3] = Yhalf[i+3] = p.vel[i];
double t = 0;
if (!curfunc.useRungeKutta()) {
boundCheck = false;
curfunc.getField(Yhalf, Y);
if (boundCheck && (!useForce || curfunc.checkBoundsWithForce())) {
p.pos[0] = -100;
return;
}
double fmult = partMult;
if (useForce) {
fmult *= .1;
for (i = 0; i != 3; i++) {
p.vel[i] += fmult*Yhalf[i];
p.pos[i] += timeStep*p.vel[i];
}
} else {
for (i = 0; i != 3; i++)
p.pos[i] += fmult*Yhalf[i];
}
for (i = 0; i != 3; i++)
Y[i] = p.pos[i];
if (curfunc.checkBounds(Y, oldY))
p.pos[0] = -100;
return;
}
boolean adapt = curfunc.useAdaptiveRungeKutta();
double h = (adapt) ? p.stepsize : 1;
int steps = 0;
double minh = .0001;
while (t >= 0 && t < 1) {
if (t+h > 1)
h = 1-t;
boundCheck = false;
// estimate one full step
rk(order, 0, Y, h);
// bail out after one iteration for some slow fields
if (!adapt)
break;
// estimate two half steps
rk(order, 0, Yhalf, h*0.5);
rk(order, 0, Yhalf, h*0.5);
if (boundCheck && (!useForce || curfunc.checkBoundsWithForce())) {
p.pos[0] = -100;
return;
}
// estimate the local error
localError = java.lang.Math.abs(Y[0] - Yhalf[0]) +
java.lang.Math.abs(Y[1] - Yhalf[1]) +
java.lang.Math.abs(Y[2] - Yhalf[2]);
if (localError > E && h > minh) {
//System.out.print(E + " " + t + " " + localError + " " + h + "\n");
h *= 0.75; // decrease the step size
if (h < minh)
h = minh;
for (i = 0; i != order; i++)
Y[i] = Yhalf[i] = oldY[i];
continue;
} else if (localError < (E * 0.5)) {
h *= 1.25; // increase the step size
if (h > maxh)
h = maxh;
}
for (i = 0; i != order; i++)
oldY[i] = Yhalf[i] = Y[i];
t += h;
steps++;
}
if (boundCheck && (!useForce || curfunc.checkBoundsWithForce())) {
p.pos[0] = -100;
return;
}
p.stepsize = h;
for (i = 0; i != 3; i++)
p.pos[i] = Y[i];
if (useForce) {
for (i = 0; i != 3; i++)
p.vel[i] = Y[i+3];
}
}
double dist2(double a[], double b[]) {
double c0 = a[0]-b[0];
double c1 = a[1]-b[1];
double c2 = a[2]-b[2];
return c0*c0+c1*c1+c2*c2;
}
void lineSegment(Particle p, int dir)
{
int numIter=0;
double maxh=20;
double error=0.0, E = .001, localError;
int order = 3;
double Y[] = rk_Y;
double Yhalf[] = rk_Yhalf;
oldY = rk_oldY;
int i;
int slice = sliceChooser.getSelectedIndex();
boolean sliced = (slice > 0);
slice -= SLICE_X;
for (i = 0; i != 3; i++)
oldY[i] = Y[i] = Yhalf[i] = p.pos[i];
double h = p.stepsize;
ls_fieldavg[0] = ls_fieldavg[1] = ls_fieldavg[2] = 0;
int steps = 0;
double minh = .1;
double segSize2min = .04*.04;
double segSize2max = .08*.08;
double lastd = 0;
int avgct = 0;
while (true) {
boundCheck = false;
steps++;
if (steps > 100) {
//System.out.print("maxsteps\n");
p.lifetime = -1;
break;
}
//System.out.print(h + " " + boundCheck + "/\n");
// estimate one full step
rk(order, 0, Y, dir*h);
// estimate two half steps
rk(order, 0, Yhalf, dir*h*0.5);
rk(order, 0, Yhalf, dir*h*0.5);
if (sliced)
Y[slice] = Yhalf[slice] = sliceval;
//System.out.print(h + " " + boundCheck + "\n");
if (boundCheck) {
for (i = 0; i != order; i++)
Y[i] = Yhalf[i] = oldY[i];
h /= 2;
if (h < minh) {
p.lifetime = -1;
break;
}
continue;
}
if (Y[0] < -1 || Y[0] >= .999 ||
Y[1] < -1 || Y[1] >= .999 ||
Y[2] < -1 || Y[2] >= .999) {
for (i = 0; i != order; i++)
Y[i] = Yhalf[i] = oldY[i];
h /= 2;
if (h < minh) {
//System.out.print("bound1\n");
p.lifetime = -1;
break;
}
continue;
}
// estimate the local error
localError = java.lang.Math.abs(Y[0] - Yhalf[0]) +
java.lang.Math.abs(Y[1] - Yhalf[1]) +
java.lang.Math.abs(Y[2] - Yhalf[2]);
if (localError > E && h > minh) {
h *= 0.75; // decrease the step size
if (h < minh)
h = minh;
for (i = 0; i != order; i++)
Y[i] = Yhalf[i] = oldY[i];
continue;
} else if (localError < (E * 0.5)) {
h *= 1.25; // increase the step size
if (h > maxh)
h = maxh;
}
double d = dist2(p.pos, Y);
if (!(d-lastd > 1e-10)) {
// we're not getting anywhere!
//System.out.print("nga " + d + " " + lastd + "\n");
p.lifetime = -1;
break;
}
if (d > segSize2max) {
h /= 2;
if (h < minh)
break;
for (i = 0; i != order; i++)
Y[i] = Yhalf[i] = oldY[i];
continue;
}
ls_fieldavg[0] += rk_k1[0];
ls_fieldavg[1] += rk_k1[1];
ls_fieldavg[2] += rk_k1[2];
avgct++;
if (d > segSize2min)
break;
lastd = d;
for (i = 0; i != order; i++)
oldY[i] = Yhalf[i] = Y[i];
}
//System.out.print(steps + " ss\n");
p.stepsize = h;
for (i = 0; i != 3; i++)
p.pos[i] = Y[i];
p.phi = java.lang.Math.sqrt(ls_fieldavg[0]*ls_fieldavg[0]+
ls_fieldavg[1]*ls_fieldavg[1]+
ls_fieldavg[2]*ls_fieldavg[2])/avgct;
}
abstract class VecFunction {
abstract String getName();
abstract VecFunction createNext();
boolean nonGradient() { return false; }
boolean useRungeKutta() { return true; }
boolean useAdaptiveRungeKutta() { return true; }
boolean checkBoundsWithForce() { return true; }
boolean noSplitFieldVectors() { return true; }
int getViewPri(double cameraPos[], double pos[]) {
return 0;
}
void render(Graphics g) {
renderItems(g, 0);
}
boolean checkBounds(double y[], double oldY[]) { return false; }
abstract void getField(double result[], double y[]);
boolean redistribute() { return true; }
void setup() {}
void setupFrame() {}
void finishFrame() {}
void actionPerformed() {}
int getBestSlice() {
double y[] = new double[3];
double r1[] = new double[3];
double r2[] = new double[3];
double r3[] = new double[3];
y[0] = y[1] = y[2] = .9;
curfunc.getField(r1, y);
y[0] = .91;
curfunc.getField(r2, y);
y[0] = .9; y[1] = .91;
curfunc.getField(r3, y);
if (r1[0] == r2[0] && r1[1] == r2[1] && r1[2] == r2[2])
return SLICE_X;
if (r1[0] == r3[0] && r1[1] == r3[1] && r1[2] == r3[2])
return SLICE_Y;
return SLICE_Z;
}
void renderSphere(Graphics g, double sz) {
// draw particles behind sphere
renderItems(g, 2);
// draw back lines of sphere
g.setColor(darkYellow);
drawSphere(g, sz, true);
// draw parts inside sphere
renderItems(g, 1);
// draw front lines of sphere
g.setColor(darkYellow);
map3d(0, 0, 0, xpoints, ypoints, 0);
int r = (int) (getScalingFactor(0, 0, 0) * sz);
g.drawOval(xpoints[0]-r, ypoints[0]-r, r*2, r*2);
drawSphere(g, sz, false);
// draw rest of particles
renderItems(g, 0);
}
};
class InverseSquaredRadial extends VecFunction {
String getName() { return BUILD_CASE_EMV("point charge",
null, "1/r^2 single"); }
void getField(double result[], double y[]) {
double r = distance(y);
if (r < chargeSize)
boundCheck = true;
if (getPot) {
result[0] = -.1/r;
return;
}
double r3 = r*r*r;
double q = .0003/r3;
result[0] = -y[0]*q;
result[1] = -y[1]*q;
result[2] = -y[2]*q;
}
static final double chargeSize = .06;
void drawCharge(Graphics g, double x, double y, double z) {
drawCharge(g, x, y, z, 0);
}
void drawCharge(Graphics g, double x, double y, double z, int dir) {
map3d(x, y, z, xpoints, ypoints, 0);
map3d(x, y, z+.3*dir*reverse, xpoints, ypoints, 1);
g.setColor(darkYellow);
int r = (int) (getScalingFactor(x, y, z) * chargeSize);
g.fillOval(xpoints[0]-r, ypoints[0]-r, r*2, r*2);
if (dir != 0)
drawArrow(g, null, xpoints[0], ypoints[0],
xpoints[1], ypoints[1], 5);
}
void render(Graphics g) {
drawCharge(g, 0, 0, 0);
renderItems(g, 1);
}
int getViewPri(double cameraPos[], double x[]) {
int i;
i = intersectSphere(cameraPos, x[0], x[1], x[2], chargeSize);
if (i == 0)
return 1;
if (i == 1)
return -1;
return 0;
}
VecFunction createNext() { return new InverseSquaredRadialDouble(); }
};
class InverseSquaredRadialDouble extends InverseSquaredRadial {
String getName() { return BUILD_CASE_EMV("point charge double", null,
"1/r^2 double"); }
double sign2;
int getBestSlice() { return SLICE_Y; }
void getField(double result[], double y[]) {
double sep = aux1Bar.getValue()/100.;
double xx1 = y[0]-sep;
double xx2 = y[0]+sep;
double r1 = distance(xx1, y[1], y[2]);
if (r1 < chargeSize)
boundCheck = true;
double r2 = distance(xx2, y[1], y[2]);
if (r2 < chargeSize)
boundCheck = true;
if (getPot) {
result[0] = -.05/r1 - .05*sign2/r2;
if (sign2 == -1)
result[0] *= 2;
return;
}
double q = .0003;
double rq1 = q/(r1*r1*r1);
double rq2 = q/(r2*r2*r2) * sign2;
result[0] = -xx1 *rq1-xx2 *rq2;
result[1] = -y[1]*rq1-y[1]*rq2;
result[2] = -y[2]*rq1-y[2]*rq2;
}
void setup() {
setXZView();
sign2 = 1;
setupBar(0, "Charge Separation", 30);
}
void render(Graphics g) {
double sep = aux1Bar.getValue()/100.;
drawCharge(g, +sep, 0, 0);
drawCharge(g, -sep, 0, 0);
renderItems(g, 1);
}
int getViewPri(double cameraPos[], double x[]) {
double sep = aux1Bar.getValue()/100.;
if (intersectSphere(cameraPos, x[0], x[1], x[2],
+sep, 0, 0, chargeSize)== 0 &&
intersectSphere(cameraPos, x[0], x[1], x[2],
-sep, 0, 0, chargeSize) == 0)
return 1;
return 0;
}
VecFunction createNext() { return
BUILD_CASE_EMV(new InverseSquaredRadialDipole(),
null, new InverseRadial()); }
};
#ifdef BUILD_E
class InverseSquaredRadialDipole extends InverseSquaredRadialDouble {
String getName() { return "dipole"; }
void setup() {
super.setup();
sign2 = -1;
}
VecFunction createNext() { return new InverseSquaredRadialQuad(); }
};
class InverseSquaredRadialQuad extends InverseSquaredRadial {
String getName() { return "quadrupole"; }
void getField(double result[], double y[]) {
double sep = aux1Bar.getValue()/100.;
double xx1 = y[0] - sep;
double xx2 = y[0] + sep;
double yy1 = y[1] - sep;
double yy2 = y[1] + sep;
double zz = y[2];
double r1 = distance(xx1, yy1, zz);
double r2 = distance(xx2, yy1, zz);
double r3 = distance(xx1, yy2, zz);
double r4 = distance(xx2, yy2, zz);
if (r1 < chargeSize || r2 < chargeSize ||
r3 < chargeSize || r4 < chargeSize)
boundCheck = true;
if (getPot) {
result[0] = .05*(-1/r1+1/r2+1/r3-1/r4);
return;
}
double q = .0003;
double rq1 = q/(r1*r1*r1);
double rq2 = q/(r2*r2*r2);
double rq3 = q/(r3*r3*r3);
double rq4 = q/(r4*r4*r4);
result[0] = -xx1*rq1-xx2*rq4+xx2*rq2+xx1*rq3;
result[1] = -yy1*rq1-yy2*rq4+yy1*rq2+yy2*rq3;
result[2] = -zz *rq1-zz *rq4+zz *rq2+zz *rq3;
}
void setup() {
super.setup();
setupBar(0, "Charge Separation", 30);
setXYViewExact();
}
void render(Graphics g) {
double sep = aux1Bar.getValue()/100.;
int i, j;
for (i = -1; i <= 1; i += 2)
for (j = -1; j <= 1; j += 2)
drawCharge(g, i*sep, j*sep, 0);
renderItems(g, 1);
}
int getViewPri(double cameraPos[], double x[]) {
double sep = aux1Bar.getValue()/100.;
int i, j;
for (i = -1; i <= 1; i += 2)
for (j = -1; j <= 1; j += 2)
if (intersectSphere(cameraPos, x[0], x[1], x[2],
i*sep, j*sep, 0, .06) != 0)
return 0;
return 1;
}
VecFunction createNext() { return new InverseRadial(); }
};
#endif
class InverseRadial extends VecFunction {
double lineLen;
String getName() { return BUILD_CASE_EMV("charged line",
null, "1/r single line"); }
void getField(double result[], double y[]) {
double r = distance(y[0], y[1], 0);
if (r < lineWidth)
boundCheck = true;
if (getPot) {
result[0] = .4*java.lang.Math.log(r+1e-20);
return;
}
double r2 = r*r;
result[0] = -.0003*y[0]/r2;
result[1] = -.0003*y[1]/r2;
result[2] = 0;
}
void setup() {
setXZView();
lineLen = 1;
}
void render(Graphics g) {
g.setColor(darkYellow);
map3d(0, 0, -lineLen, xpoints, ypoints, 0);
map3d(0, 0, +lineLen, xpoints, ypoints, 1);
g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
renderItems(g, 1);
}
int getViewPri(double cameraPos[], double x[]) {
if (intersectCylinder(cameraPos, x[0], x[1], x[2], lineWidth,
true) == 0)
return 1;
if (intersection[2] >= -lineLen && intersection[2] <= lineLen)
return 0;
return 1;
}
VecFunction createNext() { return new InverseRadialDouble(); }
};
class InverseRadialDouble extends VecFunction {
InverseRadialDouble() { sign = 1; }
String getName() { return BUILD_CASE_EMV("line charge double",
null, "1/r double lines"); }
double sign;
void getField(double result[], double y[]) {
double sep = aux1Bar.getValue()/100.;
double xx1 = y[0] - sep;
double xx2 = y[0] + sep;
double r1 = distance(xx1, y[1]);
double r2 = distance(xx2, y[1]);
if (r1 < lineWidth || r2 < lineWidth)
boundCheck = true;
if (getPot) {
result[0] = .2*(java.lang.Math.log(r1+1e-20)+
sign*java.lang.Math.log(r2+1e-20));
return;
}
double q = .0003;
double r1s = 1/(r1*r1);
double r2s = 1/(r2*r2*sign);
result[0] = q*(-xx1 *r1s-xx2 *r2s);
result[1] = q*(-y[1]*r1s-y[1]*r2s);
result[2] = 0;
}
void setup() {
setupBar(0, "Line Separation", 30);
setXZView();
}
void render(Graphics g) {
double sep = aux1Bar.getValue()/100.;
g.setColor(darkYellow);
int i;
for (i = -1; i <= 1; i += 2) {
map3d(sep*i, 0, -1, xpoints, ypoints, 0);
map3d(sep*i, 0, +1, xpoints, ypoints, 1);
g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
}
renderItems(g, 1);
}
int getViewPri(double cameraPos[], double x[]) {
int i;
double sep = aux1Bar.getValue()/100.;
for (i = -1; i <= 1; i += 2) {
if (intersectCylinder(cameraPos, x[0], x[1], x[2],
i*sep, 0, lineWidth, true) != 0)
return 0;
}
return 1;
}
VecFunction createNext() { return
BUILD_CASE_EMV(new InverseRadialDipole(), null,
new InverseRotational()); }
};
#ifdef BUILD_E
class InverseRadialDipole extends InverseRadialDouble {
InverseRadialDipole() { sign = -1; }
String getName() { return "dipole lines"; }
VecFunction createNext() { return new InverseRadialQuad(); }
};
class InverseRadialQuad extends VecFunction {
String getName() { return "quad lines"; }
void getField(double result[], double y[]) {
double sep = aux1Bar.getValue()/100.;
double xx1 = y[0] + sep;
double xx2 = y[0] - sep;
double yy1 = y[1] + sep;
double yy2 = y[1] - sep;
double r1 = distance(xx1, yy1);
double r2 = distance(xx2, yy1);
double r3 = distance(xx1, yy2);
double r4 = distance(xx2, yy2);
if (r1 < lineWidth || r2 < lineWidth ||
r3 < lineWidth || r4 < lineWidth)
boundCheck = true;
if (getPot) {
result[0] = .2*(+java.lang.Math.log(r1+1e-20)
-java.lang.Math.log(r2+1e-20)
-java.lang.Math.log(r3+1e-20)
+java.lang.Math.log(r4+1e-20));
return;
}
double q = .0003;
result[0] = q*(-xx1/(r1*r1)-xx2/(r4*r4) +xx2/(r2*r2)+xx1/(r3*r3));
result[1] = q*(-yy1/(r1*r1)-yy2/(r4*r4) +yy1/(r2*r2)+yy2/(r3*r3));
result[2] = 0;
}
void setup() {
setupBar(0, "Line Separation", 30);
setXYViewExact();
}
void render(Graphics g) {
double sep = aux1Bar.getValue()/100.;
g.setColor(darkYellow);
int i, j;
for (i = -1; i <= 1; i += 2) {
for (j = -1; j <= 1; j += 2) {
map3d(sep*i, sep*j, -1, xpoints, ypoints, 0);
map3d(sep*i, sep*j, +1, xpoints, ypoints, 1);
g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
}
}
renderItems(g, 1);
}
int getViewPri(double cameraPos[], double x[]) {
int i, j;
double sep = aux1Bar.getValue()/100.;
for (i = -1; i <= 1; i += 2) {
for (j = -1; j <= 1; j += 2) {
if (intersectCylinder(cameraPos, x[0], x[1],
x[2], i*sep, j*sep, .01, true) != 0)
return 0;
}
}
return 1;
}
VecFunction createNext() { return new FiniteChargedLine(); }
};
class FiniteChargedLine extends InverseRadial {
String getName() { return "finite line"; }
void setup() {
setXZView();
setupBar(0, "Line Length", 30);
}
void setupFrame() {
lineLen = (aux1Bar.getValue()+1) / 101.;
}
void getField(double result[], double y[]) {
result[0] = result[1] = result[2] = 0;
getLineField(result, y, 0);
}
void getLineField(double result[], double y[], double off) {
double a1 = -lineLen-y[2];
double a2 = lineLen-y[2];
double r = distance(y[0]-off, y[1]);
if (r < lineWidth && a1 <= 0 && a2 >= 0)
boundCheck = true;
double y2 = r*r;
double a12 = a1*a1;
double a22 = a2*a2;
double a12y2 = java.lang.Math.sqrt(a12+y2);
double a22y2 = java.lang.Math.sqrt(a22+y2);
if (getPot) {
result[0] -= .2*java.lang.Math.log((a2+a22y2)/(a1+a12y2));
return;
}
double q = .0001/lineLen;
double fth =
q* (-1/(a12+y2+a1*a12y2)+1/(a22+y2+a2*a22y2));
result[0] += fth*(y[0]-off);
result[1] += fth*y[1];
result[2] += q*(1/a12y2 - 1/a22y2);
}
VecFunction createNext() { return new FiniteChargedLinePair(); }
};
class FiniteChargedLinePair extends FiniteChargedLine {
double dipole;
FiniteChargedLinePair() { dipole = 1; }
String getName() { return "finite line pair"; }
void setup() {
setXZView();
setupBar(0, "Line Length", 30);
setupBar(1, "Line Separation", 30);
}
void setupFrame() {
lineLen = (aux1Bar.getValue()+1) / 101.;
}
void getField(double result[], double y[]) {
double sep = aux2Bar.getValue()/100.;
result[0] = result[1] = result[2] = 0;
getLineField(result, y, +sep);
result[0] *= dipole;
result[1] *= dipole;
result[2] *= dipole;
getLineField(result, y, -sep);
}
void render(Graphics g) {
double sep = aux2Bar.getValue()/100.;
g.setColor(darkYellow);
int i;
for (i = -1; i <= 1; i += 2) {
map3d(sep*i, 0, -lineLen, xpoints, ypoints, 0);
map3d(sep*i, 0, +lineLen, xpoints, ypoints, 1);
g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
}
renderItems(g, 1);
}
int getViewPri(double cameraPos[], double x[]) {
int i;
double sep = aux2Bar.getValue()/100.;
for (i = -1; i <= 1; i += 2) {
if (intersectCylinder(cameraPos, x[0], x[1], x[2],
i*sep, 0, lineWidth, true) != 0)
if (intersection[2] >= -lineLen &&
intersection[2] <= lineLen)
return 0;
}
return 1;
}
VecFunction createNext() { return new FiniteChargedLineDipole(); }
};
class FiniteChargedLineDipole extends FiniteChargedLinePair {
FiniteChargedLineDipole() { dipole = -1; }
String getName() { return "finite line dipole"; }
VecFunction createNext() { return new ConductingPlate(); }
};
class ConductingPlate extends VecFunction {
String getName() { return "conducting plate"; }
Complex z;
boolean plate;
double a;
ConductingPlate() {
z = new Complex();
plate = true;
}
void setupFrame() {
a = (aux1Bar.getValue()+1)/100.;
}
void getField(double result[], double y[]) {
// smythe p89
if (y[2] >= -.02 && y[2] <= .02) {
if ((plate && y[0] >= -a && y[0] <= a) ||
(!plate && (y[0] >= a || y[0] <= -a)))
boundCheck = true;
}
z.set(y[0]/a, y[2]/a);
if (y[2] < 0 && plate)
z.b = -z.b;
if (getPot) {
z.arcsin();
result[0] = (plate) ? z.b*.6*a : -z.a*.6;
return;
}
// here we calculate ((1-(z/a)^2)^(-1/2))/a, which is
// d/dz arcsin(z/a)
z.square();
z.mult(-1);
z.add(1);
z.pow(-.5);
z.mult(1/a);
if (plate) {
// field = (Im dw/dz, Re dw/dz)
result[2] = z.a * -.0007;
result[0] = z.b * -.0007;
if (y[2] < 0)
result[2] = -result[2];
} else {
// field = (Re dw/dz, -Im dw/dz)
result[0] = z.a * .0007;
result[2] = -z.b * .0007;
}
result[1] = 0;
}
int getViewPri(double cameraPos[], double x[]) {
if (intersectZPlane(cameraPos, 0, x[0], x[1], x[2]) != 0) {
if (intersection[0] >= -a && intersection[0] <= a)
return 1;
}
return 0;
}
void render(Graphics g) {
drawPlane(g, a, 1, 0);
renderItems(g, 0);
}
void setup() {
setupBar(0, "Plate Size", 60);
setXZView();
}
VecFunction createNext() { return new ChargedPlate(); }
};
class ChargedPlate extends ConductingPlate {
Complex cz;
ChargedPlate() { cz = new Complex(); }
String getName() { return "charged plate"; }
double getPot(double a1, double a2, double y) {
cz.set (y, -a1);
cz.mult(y, a2);
cz.log();
double b1 = cz.b;
cz.set (y, a1);
cz.mult(y, -a2);
cz.log();
double y2 = y*y;
return .2*(2*(a1-a2) + (b1-cz.b)*y +
a2*java.lang.Math.log(a2*a2+y2) -
a1*java.lang.Math.log(a1*a1+y2));
}
void getField(double result[], double y[]) {
if (y[2] >= -.01 && y[2] <= .01 &&
(y[0] >= -a && y[0] <= a))
boundCheck = true;
double a1 = -a-y[0];
double a2 = a-y[0];
double y2 = y[2]*y[2];
if (y2 == 0)
y2 = 1e-8;
if (getPot) {
result[0] = getPot(a1, a2, y[2]);
return;
}
double q = .0003/a;
result[0] = .5*q*
java.lang.Math.log((y2+a2*a2)/(y2+a1*a1));
result[1] = 0;
result[2] = q*
(java.lang.Math.atan(a1/y[2])-
java.lang.Math.atan(a2/y[2]));
}
VecFunction createNext() { return new ChargedPlatePair(); }
};
class ChargedPlatePair extends ChargedPlate {
String getName() { return "charged plate pair"; }
boolean useRungeKutta() { return false; }
void getField(double result[], double y[]) {
double sep = aux2Bar.getValue()/100.;
if ((y[2] >= -.01+sep && y[2] <= .01+sep ||
y[2] >= -.01-sep && y[2] <= .01-sep) &&
y[0] >= -a && y[0] <= a)
boundCheck = true;
double a1 = -a-y[0];
double a2 = a-y[0];
double y1 = y[2]-sep;
double y12 = y1*y1;
if (y12 == 0)
y12 = 1e-8;
double y2 = y[2]+sep;
double y22 = y2*y2;
if (y22 == 0)
y22 = 1e-8;
if (getPot) {
result[0] = getPot(a1, a2, y1)-
getPot(a1, a2, y2);
return;
}
double q = .0003/a;
result[0] = .5*q*
(java.lang.Math.log((y12+a2*a2)/(y12+a1*a1)) -
java.lang.Math.log((y22+a2*a2)/(y22+a1*a1)));
result[1] = 0;
result[2] = q*
(java.lang.Math.atan(a1/y1)
-java.lang.Math.atan(a2/y1)
-java.lang.Math.atan(a1/y2)
+java.lang.Math.atan(a2/y2));
}
void setup() {
setupBar(0, "Sheet Size", 30);
setupBar(1, "Sheet Separation", 33);
setXZViewExact();
}
boolean checkBounds(double y[], double oldY[]) {
double size = aux1Bar.getValue()/100.;
double sep = aux2Bar.getValue()/100.;
// check to see if particle has passed through one of plates
if (y[0] >= -size && y[0] <= size) {
if (y[2] > sep) {
if (oldY[2] < sep)
return true;
} else if (y[2] < -sep) {
if (oldY[2] > -sep)
return true;
} else if (oldY[2] > sep || oldY[2] < -sep)
return true;
}
return false;
}
void render(Graphics g) {
double sep = aux2Bar.getValue()/100.;
double size = aux1Bar.getValue()/100.;
int i;
for (i = 0; i != 2; i++) {
double s = (i == 0) ? sep : -sep;
drawPlane(g, size, 1, s);
}
renderItems(g, 1);
}
int getViewPri(double cameraPos[], double x[]) {
double sep = aux2Bar.getValue()/100.;
double size = aux1Bar.getValue()/100.;
if (intersectZPlane(cameraPos, +sep, x[0], x[1], x[2]) != 0) {
//System.out.print(size + " " + intersection[0] + " " + intersection[1] + "\n");
if (intersection[0] >= -size && intersection[0] <= size)
return 0;
}
if (intersectZPlane(cameraPos, -sep, x[0], x[1], x[2]) != 0) {
//System.out.print(size + " " + intersection[0] + " " + intersection[1] + "\n");
if (intersection[0] >= -size && intersection[0] <= size)
return 0;
}
return 1;
}
VecFunction createNext() { return new InfiniteChargedPlane(); }
};
class InfiniteChargedPlane extends VecFunction {
String getName() { return "infinite plane"; }
int getBestSlice() { return SLICE_Y; }
void getField(double result[], double y[]) {
double alpha = .0003;
if (y[2] > -.01 && y[2] < .01)
boundCheck = true;
if (getPot) {
result[0] = java.lang.Math.abs(y[2])-1;
return;
}
result[0] = 0;
result[1] = 0;
result[2] = (y[2] < 0) ? alpha : -alpha;
}
void setup() {
setXZView();
}
void render(Graphics g) {
renderItems(g, 1);
drawPlane(g, 1, 1, 0);
renderItems(g, 0);
}
int getViewPri(double cameraPos[], double x[]) {
if (intersectZPlane(cameraPos, 0, x[0], x[1], x[2]) == 0)
return 0;
return 1;
}
boolean checkBoundsWithForce() { return false; }
VecFunction createNext() { return new SphereAndPointCharge(); }
};
class SphereAndPointCharge extends InverseSquaredRadial {
String getName() { return "conducting sphere + pt"; }
double getSphereRadius() { return (aux1Bar.getValue()+1)/110.; }
double getSeparation() { return aux2Bar.getValue()/100.; }
double getSpherePos() { return getSeparation()/2; }
double getPointPos() { return -getSeparation()/2-getSphereRadius(); }
int getBestSlice() { return SLICE_Y; }
void setup() {
setupBar(0, "Sphere Size", 30);
setupBar(1, "Separation", 50);
setupBar(2, "Sphere Potential", 50);
setXZView();
}
void getField(double result[], double y[]) {
double q = -.0003;
double a = getSphereRadius();
double b = getSeparation() + a;
double spos = getSpherePos();
double imageQ = -q*a/b;
double imagePos = spos - a*a/b;
// first charge at center of sphere
double x1 = y[0]-spos;
double r1 = distance(x1, y[1], y[2]);
if (r1 < a)
boundCheck = true;
double sq = (aux3Bar.getValue()-50)*.002*a/50.;
// second charge at image position
double x2 = y[0]-imagePos;
double r2 = distance(x2, y[1], y[2]);
// third charge at point
double x3 = y[0]-getPointPos();
double r3 = distance(x3, y[1], y[2]);
if (r3 < chargeSize)
boundCheck = true;
if (getPot) {
result[0] = 400*(sq/r1 + imageQ/r2 + q/r3);
return;
}
double a1 = sq/(r1*r1*r1);
double a2 = imageQ/(r2*r2*r2);
double a3 = q/(r3*r3*r3);
result[0] = x1*a1 + x2*a2 + x3*a3;
result[1] = y[1]*(a1+a2+a3);
result[2] = y[2]*(a1+a2+a3);
}
void render(Graphics g) {
int ic = intersectSphere(cameraPos, getPointPos()-getSpherePos(),
0, 0, getSphereRadius());
if (ic != 0)
drawCharge(g, getPointPos(), 0, 0, 0);
fillSphere(g, getSphereRadius(), getSpherePos());
if (ic == 0)
drawCharge(g, getPointPos(), 0, 0, 0);
renderItems(g, 0);
}
int getViewPri(double cameraPos[], double x[]) {
if (intersectSphere(cameraPos, x[0], x[1], x[2],
getSpherePos(), 0, 0, getSphereRadius()) != 0)
return -1;
if (intersectSphere(cameraPos, x[0], x[1], x[2],
getPointPos(), 0, 0, chargeSize) != 0)
return -1;
return 0;
}
VecFunction createNext() { return new ChargedSphereAndPointCharge(); }
};
class ChargedSphereAndPointCharge extends SphereAndPointCharge {
String getName() { return "charged sphere + pt"; }
void setup() {
setupBar(0, "Sphere Size", 30);
setupBar(1, "Separation", 50);
setupBar(2, "Sphere Charge", 50);
setXZView();
}
void getField(double result[], double y[]) {
double q = -.0003;
double a = getSphereRadius();
double b = getSeparation() + a;
double spos = getSpherePos();
// first charge at center of sphere
double x1 = y[0]-spos;
double r1 = distance(x1, y[1], y[2]);
if (r1 < a)
boundCheck = true;
double sq = (aux3Bar.getValue()-50)*.0006/50.;
// second charge at point
double x3 = y[0]-getPointPos();
double r3 = distance(x3, y[1], y[2]);
if (r3 < chargeSize)
boundCheck = true;
if (getPot) {
result[0] = 300*(sq/r1 + q/r3);
return;
}
double a1 = sq/(r1*r1*r1);
double a3 = q/(r3*r3*r3);
result[0] = x1*a1 + x3*a3;
result[1] = y[1]*(a1+a3);
result[2] = y[2]*(a1+a3);
}
VecFunction createNext() { return new CylinderAndLineCharge(); }
};
class CylinderAndLineCharge extends VecFunction {
String getName() { return "cyl + line charge"; }
double getCylRadius() { return (aux1Bar.getValue()+1)/110.; }
double getSeparation() { return aux2Bar.getValue()/100.; }
double getCylPos() { return getSeparation()/2; }
double getPointPos() { return -getSeparation()/2-getCylRadius(); }
void setup() {
setupBar(0, "Cylinder Size", 30);
setupBar(1, "Separation", 30);
setupBar(2, "Cylinder Potential", 50);
setXYView();
}
void getField(double result[], double y[]) {
// Smythe p70
double q = -.0003;
double a = getCylRadius();
double b = getSeparation() + a;
double spos = getCylPos();
double imagePos = spos - a*a/b;
double x1 = y[0]-spos;
double r1 = distance(x1, y[1]);
if (r1 < a)
boundCheck = true;
double x2 = y[0]-imagePos;
double r2 = distance(x2, y[1]);
double x3 = y[0]-getPointPos();
double r3 = distance(x3, y[1]);
double chargeSize = .06;
if (r3 < chargeSize)
boundCheck = true;
// pick a charge at the center of the cylinder that puts it
// at ground potential
double cq = q*(java.lang.Math.exp(b-a)-1) +
(aux3Bar.getValue()/50.-1)*a*.0006;
if (getPot) {
result[0] = -700*(cq*java.lang.Math.log(r1+1e-20)
-q*java.lang.Math.log(r2+1e-20)
+q*java.lang.Math.log(r3+1e-20));
return;
}
double a1 = cq/(r1*r1);
double a2 = -q/(r2*r2);
double a3 = q/(r3*r3);
result[0] = x1*a1 + x2*a2 + x3*a3;
result[1] = y[1]*(a1+a2+a3);
result[2] = 0;
}
void render(Graphics g) {
int ic = intersectCylinder(cameraPos, getPointPos(), 0, 0,
getCylPos(), 0, getCylRadius(), false);
if (ic == 0)
fillCylinder(g, getCylRadius(), getCylPos());
g.setColor(darkYellow);
map3d(getPointPos(), 0, -1, xpoints, ypoints, 0);
map3d(getPointPos(), 0, +1, xpoints, ypoints, 1);
g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
if (ic != 0)
fillCylinder(g, getCylRadius(), getCylPos());
renderItems(g, 0);
}
int getViewPri(double cameraPos[], double x[]) {
if (intersectCylinder(cameraPos, x[0], x[1], x[2],
getPointPos(), 0, lineWidth, true) != 0)
return -1;
if (intersectCylinder(cameraPos, x[0], x[1], x[2],
getCylPos(), 0, getCylRadius(), true) != 0)
return -1;
return 0;
}
VecFunction createNext() { return new SphereInField(); }
};
class SphereInField extends VecFunction {
SphereInField() { conducting = true; showD = false; }
String getName() { return "conducting sphere in field"; }
boolean conducting;
boolean showD;
int getBestSlice() { return SLICE_Y; }
void getField(double result[], double y[]) {
// marion p74
double a = aux1Bar.getValue()/100.;
double a3 = a*a*a;
double r = distance(y);
double e1 = aux2Bar.getValue()/10. + 1;
double dimult = (conducting) ? 1 : (e1-1)/(e1+2);
double fmult = .0006;
if (r < a) {
result[0] = result[1] = 0;
if (conducting)
boundCheck = true;
else {
if (getPot)
result[0] = -(1-dimult)*y[2];
else
result[2] = (showD) ? e1*fmult*(1-dimult) :
fmult*(1-dimult);
}
return;
}
double costh = y[2]/r;
double sinth = java.lang.Math.sqrt(1-costh*costh);
double cosph = y[0]/(r*sinth);
double sinph = y[1]/(r*sinth);
double r_3 = 1/(r*r*r);
if (getPot) {
result[0] = -(1-dimult*a3*r_3)*y[2];
return;
}
double er = (1+dimult*2*a3*r_3)*costh*fmult;
double eth = -(1-dimult*a3*r_3)*sinth*fmult;
er /= r;
result[0] = y[0]*er + eth*costh*cosph;
result[1] = y[1]*er + eth*costh*sinph;
result[2] = y[2]*er - eth*sinth;
}
void setup() {
setupBar(0, "Sphere Size", 60);
setXZViewExact();
}
void render(Graphics g) {
double a = aux1Bar.getValue()/100.;
fillSphere(g, a, 0);
renderItems(g, 0);
}
int getViewPri(double cameraPos[], double x[]) {
double a = aux1Bar.getValue()/100.;
return intersectSphere(cameraPos, x[0], x[1], x[2], a);
}
VecFunction createNext() { return new DielectricSphereInFieldE(); }
};
class DielectricSphereInFieldE extends SphereInField {
DielectricSphereInFieldE() { conducting = false; showD = false; }
String getName() { return "dielec sphere in field E"; }
void setup() {
setupBar(0, "Sphere Size", 60);
setupBar(1, "Dielectric Strength", 60);
setXZViewExact();
}
void render(Graphics g) {
double a = aux1Bar.getValue()/100.;
renderSphere(g, a);
}
int getViewPri(double cameraPos[], double x[]) {
double a = aux1Bar.getValue()/100.;
return intersectSphere(cameraPos, x[0], x[1], x[2], a);
}
boolean noSplitFieldVectors() { return false; }
VecFunction createNext() { return new DielectricSphereInFieldD(); }
};
class DielectricSphereInFieldD extends DielectricSphereInFieldE {
DielectricSphereInFieldD() { conducting = false; showD = true; }
String getName() { return "dielec sphere in field D"; }
VecFunction createNext() { return new CylinderInField(); }
};
class CylinderInField extends VecFunction {
CylinderInField() { conducting = true; showD = false; }
String getName() { return "cylinder in field"; }
boolean conducting, showD;
double a;
void setupFrame() {
a = aux1Bar.getValue()/100.;
}
void getField(double result[], double y[]) {
// smythe p67
double a2 = a*a;
double r = distance(y[0], y[1]);
double e1 = aux2Bar.getValue()/10. + 1;
double dimult = (conducting) ? 1 : (e1-1)/(e1+1);
double fmult = .0006;
if (r < a) {
result[0] = result[1] = result[2] = 0;
if (conducting)
boundCheck = true;
else if (getPot)
result[0] = -(1-dimult)*y[0];
else
result[0] = (showD) ? e1*fmult*(1-dimult) :
fmult*(1-dimult);
return;
}
double costh = y[0]/r;
double sinth = y[1]/r;
double r_2 = 1/(r*r);
if (getPot) {
result[0] = -(1-dimult*a2*r_2)*y[0];
return;
}
double er = (1+dimult*a2*r_2)*costh*fmult;
double eth = -(1-dimult*a2*r_2)*sinth*fmult;
er /= r;
result[0] = y[0]*er - eth*sinth;
result[1] = y[1]*er + eth*costh;
result[2] = 0;
}
void setup() {
setupBar(0, "Cylinder Size", 40);
setXYView();
}
void render(Graphics g) {
fillCylinder(g, a, 0);
renderItems(g, 0);
}
int getViewPri(double cameraPos[], double x[]) {
return intersectCylinder(cameraPos, x[0], x[1], x[2],
a, conducting);
}
VecFunction createNext() { return new DielectricCylinderInFieldE(); }
};
class DielectricCylinderInFieldE extends CylinderInField {
DielectricCylinderInFieldE() { conducting = false; showD = false; }
String getName() { return "dielec cyl in field E"; }
void setup() {
setupBar(0, "Cylinder Size", 40);
setupBar(1, "Dielectric Strength", 60);
setXYView();
}
void render(Graphics g) {
// draw particles behind cylinder
renderItems(g, 2);
// draw back faces of cylinder
g.setColor(darkYellow);
drawCylinder(g, a, 0, true);
// draw particles inside
renderItems(g, 1);
// draw front faces
g.setColor(darkYellow);
drawCylinder(g, a, 0, false);
// draw particles in front
renderItems(g, 0);
}
boolean noSplitFieldVectors() { return false; }
VecFunction createNext() { return new DielectricCylinderInFieldD(); }
};
class DielectricCylinderInFieldD extends DielectricCylinderInFieldE {
DielectricCylinderInFieldD() { conducting = false; showD = true; }
String getName() { return "dielec cyl in field D"; }
VecFunction createNext() { return new DielectricBoundaryE(); }
};
class DielectricBoundaryE extends InverseSquaredRadial {
boolean showD, conducting;
double planeZ;
DielectricBoundaryE() { conducting = false; showD = false; }
int getBestSlice() { return SLICE_Y; }
String getName() { return "dielec boundary E"; }
void setup() {
setupBar(0, "Charge Location", 60);
if (!conducting)
setupBar(1, "Dielectric Strength", 60);
setViewMatrix(0, pi/40-pi/2);
}
void render(Graphics g) {
renderItems(g, 1);
drawPlane(g, 1, 1, planeZ);
double cx = aux1Bar.getValue()/50.-1.001;
drawCharge(g, 0, 0, cx);
renderItems(g, 0);
}
int getViewPri(double cameraPos[], double x[]) {
double cx = aux1Bar.getValue()/50.-1.001;
if (intersectSphere(cameraPos, x[0], x[1], x[2]-cx,
chargeSize) == 0
&& intersectZPlane(cameraPos, 0, x[0], x[1], x[2]-planeZ) == 0)
return 0;
return 1;
}
void getField(double result[], double y[]) {
double cx = aux1Bar.getValue()/50.-1.001;
double r1 = distance(y[0], y[1], y[2]-cx);
if (r1 < chargeSize)
boundCheck = true;
double eps1 = 1;
double eps2 = aux2Bar.getValue()/10. + 1;
if (conducting)
eps2 = 1e8;
if (cx < planeZ) {
eps1 = eps2;
eps2 = 1;
}
double q1 = .0003;
double q2 = -(eps2-eps1)/(eps2+eps1)*q1;
double ep = eps1;
if (cx > planeZ && y[2] < planeZ || cx < planeZ && y[2] > planeZ) {
q1 = 2*eps2*q1/(eps2+eps1);
q2 = 0;
ep = eps2;
}
double r2 = distance(y[0], y[1], y[2]-planeZ*2+cx);
if (getPot) {
result[0] = -1000*(q1/(r1*ep) + q2/(r2*ep));
return;
}
if (!showD) {
q1 /= ep;
q2 /= ep;
}
double rq1 = q1/(r1*r1*r1);
double rq2 = q2/(r2*r2*r2);
result[0] = -y[0]*(rq1+rq2);
result[1] = -y[1]*(rq1+rq2);
result[2] = -(y[2]-cx)*rq1-(y[2]-planeZ*2+cx)*rq2;
}
VecFunction createNext() { return new DielectricBoundaryD(); }
};
class DielectricBoundaryD extends DielectricBoundaryE {
DielectricBoundaryD() { conducting = false; showD = true; }
String getName() { return "dielec boundary D"; }
VecFunction createNext() { return new ConductingPlane(); }
};
class ConductingPlane extends DielectricBoundaryE {
ConductingPlane() { conducting = true; showD = false; planeZ = -1; }
String getName() { return "conducting plane + pt"; }
VecFunction createNext() { return new FastChargeEField(); }
};
class FastChargeEField extends MovingChargeField {
String getName() { return "fast charge"; }
int getBestSlice() { return SLICE_Y; }
void getField(double result[], double y[]) {
double rz = distance(y);
if (rz < chargeSize)
boundCheck = true;
double r = distance(y[0], y[1]);
// sine of angle between particle vector and direction of motion
double sinth = r/rz;
double beta = (aux1Bar.getValue()+1)/102.;
if (getPot) {
result[0] = -.1/
(rz*java.lang.Math.pow(1-beta*beta*sinth*sinth, .5));
return;
}
// field = e R (1-beta^2) / R^3 (1-beta^2 sin^2 th)^(3/2)
double b = -.0001 * (1-beta*beta)/
(rz*rz*rz*java.lang.Math.pow(1-beta*beta*sinth*sinth, 1.5));
result[0] = b*y[0];
result[1] = b*y[1];
result[2] = b*y[2];
}
void setup() {
setupBar(0, "Speed/C", 60);
super.setup();
}
boolean nonGradient() { return false; }
VecFunction createNext() { return new ChargedRing(); }
};
class ChargedRing extends CurrentLoopField {
ChargedRing() { useColor = false; }
String getName() { return "charged ring"; }
int getBestSlice() { return SLICE_Y; }
void getField(double result[], double y[]) {
getLoopField(result, y, 0);
}
void getLoopField(double result[], double y[], double zoff) {
double xx = y[0];
double yy = y[1];
double zz = y[2]+zoff;
int i;
result[0] = result[1] = result[2] = 0;
double size = aux1Bar.getValue()/100.;
int ct = 8;
double q = (getPot) ? .2/ct : -.0006/ct;
double ang0 = java.lang.Math.atan2(y[1], y[0]);
for (i = 0; i != ct; i++) {
double ang = pi*2*i/ct;
double jxx = size*java.lang.Math.cos(ang+ang0);
double jyy = size*java.lang.Math.sin(ang+ang0);
double rx = xx-jxx;
double ry = yy-jyy;
double rz = zz;
double r = java.lang.Math.sqrt(rx*rx+ry*ry+rz*rz);
if (r < .06)
boundCheck = true;
double r3 = r*r*r;
if (getPot) {
result[0] += -q/r;
} else {
result[0] += q*rx/r3;
result[1] += q*ry/r3;
result[2] += q*rz/r3;
}
}
}
void setup() {
setupBar(0, "Ring Size", 40);
setXZView();
}
boolean nonGradient() { return false; }
VecFunction createNext() { return new ChargedRingPair(); }
};
class ChargedRingPair extends ChargedRing {
String getName() { return "charged ring pair"; }
double sep;
int r2;
double tres1[], tres2[];
ChargedRingPair() {
tres1 = new double[3];
tres2 = new double[3];
r2 = 1;
}
void setupFrame() {
sep = aux2Bar.getValue()/100.;
}
void getField(double result[], double y[]) {
getLoopField(tres1, y, -sep);
getLoopField(tres2, y, sep);
int i;
for (i = 0; i != 3; i++)
result[i] = tres1[i] + r2*tres2[i];
}
void render(Graphics g) {
renderItems(g, 0);
double size = aux1Bar.getValue()/100.;
renderLoop(g, 0, -sep, 1, size);
renderLoop(g, 0, sep, 1, size);
renderItems(g, 1);
}
void setup() {
setupBar(0, "Ring Size", 40);
setupBar(1, "Ring Separation", 40);
setXZView();
}
VecFunction createNext() { return new ChargedRingDipole(); }
};
class ChargedRingDipole extends ChargedRingPair {
String getName() { return "charged ring dipole"; }
ChargedRingDipole() {
r2 = -1;
}
VecFunction createNext() { return new SlottedPlane(); }
}
class SlottedPlane extends VecFunction {
String getName() { return "slotted conducting plane"; }
Complex z, z2;
SlottedPlane() {
z = new Complex();
z2 = new Complex();
}
void getField(double result[], double y[]) {
// W = -.5E (z +- (z^2-a^2)^1/2)
// dw/dz = -.5E (1 +- z/(z^2-a^2)^1/2)
// Smythe p92
double a = (aux1Bar.getValue()+1)/101.;
if (y[2] >= -.01 && y[2] <= .01 &&
(y[0] < -a || y[0] > a))
boundCheck = true;
z.set(y[0], y[2]);
z2.set(z);
z2.square();
z2.add(-a*a);
if (getPot) {
z2.pow(.5);
if (z2.b < 0)
z2.mult(-1);
z2.add(z.a, z.b);
result[0] = -z2.b*.6;
return;
}
z2.pow(-.5);
// I can barely understand what Smythe is talking about but
// I think he wants the square root to always have a positive
// imaginary part. Here we already took the reciprocal
// (by calling pow(-.5) instead of pow(.5)) so we want the
// root to have a negative imaginary part instead.
if (z2.b > 0)
z2.mult(-1);
z2.mult(z);
// field = (Im dw/dz, Re dw/dz)
result[2] = (1+z2.a) * .003;
result[0] = (z2.b) * .003;
result[1] = 0;
}
void setup() {
setupBar(0, "Slot Size", 60);
setXZView();
}
int getViewPri(double cameraPos[], double x[]) {
if (intersectZPlane(cameraPos, 0, x[0], x[1], x[2]) != 0) {
double a = (aux1Bar.getValue()+1)/101.;
if (intersection[0] < -a || intersection[0] > a)
return 1;
}
return 0;
}
void render(Graphics g) {
g.setColor(darkYellow);
double a = (aux1Bar.getValue()+1)/101.;
map3d(-1, -1, 0, xpoints, ypoints, 0);
map3d(-a, -1, 0, xpoints, ypoints, 1);
map3d(-a, +1, 0, xpoints, ypoints, 2);
map3d(-1, +1, 0, xpoints, ypoints, 3);
g.fillPolygon(xpoints, ypoints, 4);
map3d( 1, -1, 0, xpoints, ypoints, 0);
map3d( a, -1, 0, xpoints, ypoints, 1);
map3d( a, +1, 0, xpoints, ypoints, 2);
map3d( 1, +1, 0, xpoints, ypoints, 3);
g.fillPolygon(xpoints, ypoints, 4);
renderItems(g, 0);
}
VecFunction createNext() { return new PlanePair(); }
};
class PlanePair extends ConductingPlate {
String getName() { return "conducting planes w/ gap"; }
PlanePair() { super(); plate = false; }
void setup() {
setupBar(0, "Gap Size", 20);
setXZView();
}
int getViewPri(double cameraPos[], double x[]) {
if (intersectZPlane(cameraPos, 0, x[0], x[1], x[2]) != 0) {
if (intersection[0] < -a || intersection[0] > a)
return 1;
}
return 0;
}
void render(Graphics g) {
g.setColor(darkYellow);
map3d(-1, -1, 0, xpoints, ypoints, 0);
map3d(-a, -1, 0, xpoints, ypoints, 1);
map3d(-a, +1, 0, xpoints, ypoints, 2);
map3d(-1, +1, 0, xpoints, ypoints, 3);
g.fillPolygon(xpoints, ypoints, 4);
map3d( 1, -1, 0, xpoints, ypoints, 0);
map3d( a, -1, 0, xpoints, ypoints, 1);
map3d( a, +1, 0, xpoints, ypoints, 2);
map3d( 1, +1, 0, xpoints, ypoints, 3);
g.fillPolygon(xpoints, ypoints, 4);
renderItems(g, 0);
}
VecFunction createNext() { return null; }
};
#endif
#ifndef BUILD_E
class InverseRotational extends InverseRadial {
String getName() { return BUILD_CASE_EMV(null, "current line",
"1/r rotational"); }
void setup() {
setXYViewExact();
}
void getField(double result[], double y[]) {
double r = distance(y[0], y[1]);
if (showA) {
result[0] = result[1] = 0;
result[2] = -.001*(java.lang.Math.log(r)-.5);
} else {
if (r < lineWidth*2)
boundCheck = true;
rotateParticle(result, y, .0001/(r*r));
}
}
void render(Graphics g) {
g.setColor(darkYellow);
map3d(0, 0, -1, xpoints, ypoints, 0);
map3d(0, 0, +1, xpoints, ypoints, 1);
drawCurrentLine(g, xpoints[0], ypoints[0], xpoints[1], ypoints[1],
12, true, 1);
renderItems(g, 1);
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new InverseRotationalDouble(); }
};
class InverseRotationalDouble extends InverseRadialDouble {
InverseRotationalDouble() { dir2 = 1; ext = false; }
int dir2;
boolean ext;
String getName() { return BUILD_CASE_EMV(null, "current line double",
"1/r rotational double"); }
void getField(double result[], double y[]) {
double sep = aux1Bar.getValue()/100.;
double r = distance(y[0] - sep, y[1]);
double r2 = distance(y[0] + sep, y[1]);
if (ext) {
double p = aux3Bar.getValue()*pi/50.;
double s = aux2Bar.getValue()/30.;
getDirectionField(result, y, pi/2, p);
result[0] *= s;
result[1] *= s;
result[2] *= s;
} else
result[0] = result[1] = result[2] = 0;
if (showA) {
if (dir2 == 1)
result[2] += -.001*(java.lang.Math.log(r)+
java.lang.Math.log(r2)-1);
else
result[2] += -.001*(java.lang.Math.log(r)-
java.lang.Math.log(r2));
} else {
if (r < lineWidth*2)
boundCheck = true;
rotateParticleAdd(result, y, .0001/(r*r), sep, 0);
if (r2 < lineWidth*2)
boundCheck = true;
rotateParticleAdd(result, y, dir2*.0001/(r2*r2), -sep, 0);
}
}
void setup() {
setupBar(0, "Line Separation", 30);
if (ext) {
setupBar(1, "Ext. Strength", 28);
setupBar(2, "Ext. Direction", 0);
}
setXYViewExact();
}
void render(Graphics g) {
double sep = aux1Bar.getValue()/100.;
g.setColor(darkYellow);
int i;
for (i = -1; i <= 1; i += 2) {
map3d(sep*i, 0, -1, xpoints, ypoints, 0);
map3d(sep*i, 0, +1, xpoints, ypoints, 1);
int dir = (i == -1) ? dir2 : 1;
drawCurrentLine(g, xpoints[0], ypoints[0],
xpoints[1], ypoints[1], 12, true, dir);
}
renderItems(g, 1);
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new InverseRotationalDoubleExt(); }
};
class InverseRotationalDoubleExt extends InverseRotationalDouble {
InverseRotationalDoubleExt() { ext = true; }
String getName() { return BUILD_CASE_EMV(null, "cur line double + ext",
"1/r rot double + ext"); }
VecFunction createNext() { return new InverseRotationalDipole(); }
};
class InverseRotationalDipole extends InverseRotationalDouble {
InverseRotationalDipole() { dir2 = -1; }
String getName() { return BUILD_CASE_EMV(null, "current line dipole",
"1/r rotational dipole"); }
VecFunction createNext() { return new InverseRotationalDipoleExt(); }
};
class InverseRotationalDipoleExt extends InverseRotationalDouble {
InverseRotationalDipoleExt() { dir2 = -1; ext = true; }
void setup() {
super.setup();
aux2Bar.setValue(17);
aux3Bar.setValue(25);
}
String getName() { return BUILD_CASE_EMV(null, "cur line dipole + ext",
"1/r rot dipole + ext"); }
VecFunction createNext() { return new OneDirectionFunction(); }
};
class OneDirectionFunction extends VecFunction {
String getName() { return BUILD_CASE_EMV(null, "uniform field",
"one direction"); }
void getField(double result[], double y[]) {
double th = aux1Bar.getValue() * pi /50.;
double ph = aux2Bar.getValue() * pi /50.;
getDirectionField(result, y, th, ph);
}
void setup() {
setupBar(0, "Theta", 25);
setupBar(1, "Phi", 0);
setXYView();
}
VecFunction createNext() { return
BUILD_CASE_EMV(null, new MovingChargeField(),
new InverseSquaredRadialSphere()); }
};
void getDirectionField(double result[], double y[],
double th, double ph) {
double sinth = java.lang.Math.sin(th);
double costh = java.lang.Math.cos(th);
double sinph = java.lang.Math.sin(ph);
double cosph = java.lang.Math.cos(ph);
if (!showA) {
if (getPot) {
result[0] = -.4*(y[0]*sinth*cosph + y[1]*sinth*sinph +
y[2]*costh);
return;
}
result[0] = .0003*sinth*cosph;
result[1] = .0003*sinth*sinph;
result[2] = .0003*costh;
} else {
// The A for this field is a linear rotation around
// an axis pointing in the direction of the field.
// First calculate the axis.
double axis[] = new double[3];
axis[0] = sinth*cosph;
axis[1] = sinth*sinph;
axis[2] = costh;
// now project the point we are looking at onto the axis
double d = dot(axis, y);
// subtract out this projection so we can get the vector
// from the point to the axis
double r[] = new double[3];
int i;
for (i = 0; i != 3; i++)
r[i] = .0006*(y[i] - axis[i]*d);
// cross this vector with the axis vector to get the result
cross(result, axis, r);
}
}
#endif
#ifndef BUILD_V
class MovingChargeField extends InverseSquaredRadial {
String getName() { return "moving charge"; }
void getField(double result[], double y[]) {
double rz = distance(y);
if (showA) {
result[0] = result[1] = 0;
result[2] = .0003/rz;
} else {
double r = distance(y[0], y[1]);
if (rz < chargeSize)
boundCheck = true;
rotateParticle(result, y, .0001/(rz*rz*rz));
}
}
void render(Graphics g) {
drawCharge(g, 0, 0, 0, 1);
renderItems(g, 1);
}
void setup() {
setXZView();
}
boolean nonGradient() { return true; }
VecFunction createNext() { return
BUILD_CASE_EMV(null, new FastChargeField(), null); }
};
#endif
#ifdef BUILD_M
class FastChargeField extends MovingChargeField {
String getName() { return "fast charge"; }
double getFieldStrength(double y[]) {
double rz = distance(y);
if (rz < chargeSize)
boundCheck = true;
double r = distance(y[0], y[1]);
// sine of angle between particle vector and direction of motion
double sinth = r/rz;
double beta = (aux1Bar.getValue()+1)/102.;
// field = e(beta X R)(1-beta^2)/(R^3)(1-beta^2 sin^2 th)^(3/2)
// = C/(R^2)(1-beta^2 sin^2 th)^(3/2)
// We are ignoring C which affects the strength of the field and
// does not change its shape. The field is in the direction
// of beta X R which means that it rotates the particle
// around the z axis. XXX changed this, fix
double b = .001*(1-beta*beta)*beta/
(rz*rz*java.lang.Math.pow(1-beta*beta*sinth*sinth, 1.5));
return b;
}
void setup() {
super.setup();
setupBar(0, "Speed/C", 60);
}
void render(Graphics g) {
// the charge is always going in the same direction,
// regardless of reverse
drawCharge(g, 0, 0, 0, reverse);
renderItems(g, 1);
}
void getField(double result[], double y[]) {
if (showA) {
double rz = distance(y);
double r = distance(y[0], y[1]);
// sine of angle between particle vector and direction of motion
double sinth = r/rz;
double beta = (aux1Bar.getValue()+1)/102.;
result[0] = result[1] = 0;
result[2] = .003*beta/
(rz*java.lang.Math.pow(1-beta*beta*sinth*sinth, .5));
} else
rotateParticle(result, y, getFieldStrength(y));
}
VecFunction createNext() { return new MovingChargeFieldDouble(); }
};
class MovingChargeFieldDouble extends InverseSquaredRadialDouble {
String getName() { return "moving charge double"; }
MovingChargeFieldDouble() { dir2 = 1; }
int dir2;
void getField(double result[], double y[]) {
result[0] = result[1] = result[2] = 0;
double sep = aux1Bar.getValue()/100.;
double rz1 = distance(y[0] - sep, y[1], y[2]);
double rz2 = distance(y[0] + sep, y[1], y[2]);
if (showA) {
result[0] = result[1] = 0;
result[2] = .0003*(1/rz1 + dir2/rz2);
} else {
double r = distance(y[0] - sep, y[1]);
if (rz1 < chargeSize)
boundCheck = true;
rotateParticleAdd(result, y, .0001/(rz1*rz1*rz1), sep, 0);
if (rz2 < chargeSize)
boundCheck = true;
r = distance(y[0] + sep, y[1]);
rotateParticleAdd(result, y, dir2*.0001/(rz2*rz2*rz2),
-sep, 0);
}
}
void setup() {
setupBar(0, "Charge Separation", 30);
super.setup();
}
void render(Graphics g) {
double sep = aux1Bar.getValue()/100.;
drawCharge(g, +sep, 0, 0, 1);
drawCharge(g, -sep, 0, 0, dir2);
renderItems(g, 1);
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new MovingChargeDipole(); }
};
class MovingChargeDipole extends MovingChargeFieldDouble {
MovingChargeDipole() { dir2 = -1; }
String getName() { return "moving charge dipole"; }
VecFunction createNext() { return new CurrentLoopField(); }
};
#endif
#ifndef BUILD_V
class CurrentLoopField extends VecFunction {
CurrentLoopField() { useColor = true; }
Color colors[];
boolean useColor;
double size;
String getName() { return "current loop"; }
boolean useAdaptiveRungeKutta() { return false; }
void setup() {
setXZView();
setupBar(0, "Loop Size", 40);
}
void setupFrame() {
size = (aux1Bar.getValue()+1)/100.;
}
void getField(double result[], double y[]) {
getLoopField(result, y, 0, 0, 1, size);
}
void getLoopField(double result[], double y[], double xoff,
double zoff, int dir, double size) {
double xx = y[0]-xoff;
double yy = y[1];
double zz = y[2]-zoff;
int i;
result[0] = result[1] = result[2] = 0;
int ct = 8;
double q = .0006*dir/(size*ct);
double ang0 = java.lang.Math.atan2(y[1], y[0]);
for (i = 0; i != ct; i ++) {
double ang = pi*2*i/ct;
double jxx = size*java.lang.Math.cos(ang+ang0);
double jyy = size*java.lang.Math.sin(ang+ang0);
double lxx = -jyy*q;
double lyy = jxx*q;
double rx = xx-jxx;
double ry = yy-jyy;
double rz = zz;
double r = java.lang.Math.sqrt(rx*rx+ry*ry+rz*rz);
if (!showA) {
double r3 = r*r*r;
if (r < .04 && useMagnetMove())
boundCheck = true;
// dl x R = (lxx, lyy, 0) X R
// = (lyy*rz, -lxx*rz, lxx*ry-lyy*rx)
// we are integrating by dl x Rhat/r^2, so we rewrite
// that as dl x R / r^3.
double cx = lyy*rz/r3;
double cy = -lxx*rz/r3;
double cz = (lxx*ry - lyy*rx)/r3;
result[0] += cx;
result[1] += cy;
result[2] += cz;
} else {
// dl /r = lxx
result[0] += 6*lxx/r;
result[1] += 6*lyy/r;
}
}
}
boolean checkBounds(double y[], double oldY[]) {
if (!useMagnetMove())
return false;
if ((y[2] > 0 && oldY[2] < 0) ||
(y[2] < 0 && oldY[2] > 0)) {
double r = java.lang.Math.sqrt(y[0]*y[0]+y[1]*y[1]);
if (r < size)
return true;
}
return false;
}
void render(Graphics g) {
renderItems(g, 1);
renderLoop(g, 0, 0, 1, size);
renderItems(g, 0);
}
void renderLoop(Graphics g, double xoff, double zoff, int dir,
double size) {
final int loopSegments = 72;
int i;
if (!useColor)
g.setColor(darkYellow);
for (i = 0; i != loopSegments; i++) {
double ang1 = pi*2*i/loopSegments;
double ang2 = pi*2*(i+dir)/loopSegments;
double jxx1 = size*java.lang.Math.cos(ang1) + xoff;
double jyy1 = size*java.lang.Math.sin(ang1);
double jxx2 = size*java.lang.Math.cos(ang2) + xoff;
double jyy2 = size*java.lang.Math.sin(ang2);
map3d(jxx1, jyy1, zoff, xpoints, ypoints, 0);
map3d(jxx2, jyy2, zoff, xpoints, ypoints, 1);
if (useColor)
g.setColor(getCurrentColor(i*dir));
if (i == 0 && useColor)
drawCurrentArrow(g, xpoints[0], ypoints[0],
xpoints[1], ypoints[1]);
else
g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
}
}
int getViewPri(double cameraPos[], double x[]) {
if (intersectZPlane(cameraPos, 0, x[0], x[1], x[2]) != 0)
return 1;
return 0;
}
boolean noSplitFieldVectors() { return false; }
boolean nonGradient() { return true; }
VecFunction createNext() { return
BUILD_CASE_EMV(null, new CurrentLoopsSideField(), null); }
};
#endif
#ifdef BUILD_M
class CurrentLoopsSideField extends CurrentLoopField {
String getName() { return "loop pair"; }
int dir2;
double offx, offz, size;
double tres1[], tres2[];
CurrentLoopsSideField() {
tres1 = new double[3];
tres2 = new double[3];
}
void setup() {
setXZView();
setupBar(0, "Loop Size", 40);
setupBar(1, "Loop Separation", 10);
setupBar(2, "Offset", 0);
}
void setupFrame() {
size = (aux1Bar.getValue()+1)/100.;
double sep = aux2Bar.getValue()/100.;
double sep2 = aux3Bar.getValue()/100.;
offx = sep*(1-size)+size; offz = sep2;
dir2 = 1;
}
void getField(double result[], double y[]) {
getLoopField(tres1, y, +offx, +offz, 1, size);
getLoopField(tres2, y, -offx, -offz, dir2, size);
int i;
for (i = 0; i != 3; i++)
result[i] = tres1[i] + tres2[i];
}
void render(Graphics g) {
renderItems(g, 0);
renderLoop(g, +offx, +offz, 1, size);
renderLoop(g, -offx, -offz, dir2, size);
renderItems(g, 1);
}
boolean checkBounds(double y[], double oldY[]) {
if (!useMagnetMove())
return false;
if ((y[2] > offz && oldY[2] < offz) ||
(y[2] < offz && oldY[2] > offz)) {
double x = y[0]-offx;
double r = java.lang.Math.sqrt(x*x+y[1]*y[1]);
if (r < size)
return true;
}
if ((y[2] > -offz && oldY[2] < -offz) ||
(y[2] < -offz && oldY[2] > -offz)) {
double x = y[0]+offx;
double r = java.lang.Math.sqrt(x*x+y[1]*y[1]);
if (r < size)
return true;
}
return false;
}
VecFunction createNext() { return new CurrentLoopsSideOppField(); }
};
class CurrentLoopsSideOppField extends CurrentLoopsSideField {
String getName() { return "loop pair opposing"; }
void setupFrame() {
size = (aux1Bar.getValue()+1)/100.;
double sep = aux2Bar.getValue()/100.;
double sep2 = aux3Bar.getValue()/100.;
offx = sep*(1-size)+size; offz = sep2;
dir2 = -1;
}
VecFunction createNext() { return new CurrentLoopsStackedField(); }
};
class CurrentLoopsStackedField extends CurrentLoopsSideField {
String getName() { return "loop pair stacked"; }
void setupFrame() {
size = (aux1Bar.getValue()+1)/100.;
double sep = (aux2Bar.getValue()+1)/100.;
double sep2 = aux3Bar.getValue()/100.;
offx = sep2; offz = sep;
dir2 = 1;
}
VecFunction createNext() { return new CurrentLoopsStackedOppField(); }
};
class CurrentLoopsStackedOppField extends CurrentLoopsSideField {
String getName() { return "loop pair stacked, opp."; }
void setupFrame() {
size = (aux1Bar.getValue()+1)/100.;
double sep = (aux2Bar.getValue()+1)/100.;
double sep2 = aux3Bar.getValue()/100.;
offx = sep2; offz = sep;
dir2 = -1;
}
VecFunction createNext() { return new CurrentLoopsOpposingConcentric(); }
};
class CurrentLoopsOpposingConcentric extends CurrentLoopField {
String getName() { return "concentric loops"; }
int dir2;
double tres1[], tres2[];
double size2;
CurrentLoopsOpposingConcentric() {
tres1 = new double[3];
tres2 = new double[3];
}
void setup() {
setXZView();
setupBar(0, "Outer Loop Size", 75);
setupBar(1, "Inner Loop Size", 50);
}
void setupFrame() {
size = (aux1Bar.getValue()+1)/101.;
size2 = size*(aux2Bar.getValue()+1)/101.;
}
void getField(double result[], double y[]) {
getLoopField(tres1, y, 0, 0, 1, size);
getLoopField(tres2, y, 0, 0, -1, size2);
// getLoopField's result is not proportional to size^2 (as
// it should be) but rather to size, because that makes
// the field of small loops too hard to see, and because
// we care more about the shape of the field than its
// absolute magnitude. To correct for that, we have to
// multiply the smaller loop's field by size2/size to
// ensure that the field looks correct for two concentric
// loops with the same current.
double mult = size2/size;
int i;
for (i = 0; i != 3; i++)
result[i] = tres1[i] + mult*tres2[i];
}
void render(Graphics g) {
renderItems(g, 0);
renderLoop(g, 0, 0, 1, size);
renderLoop(g, 0, 0, -1, size2);
renderItems(g, 1);
}
VecFunction createNext() { return new SolenoidField(); }
};
class SolenoidField extends VecFunction {
String getName() { return "solenoid"; }
boolean useRungeKutta() { return false; }
double height, size;
int turns;
void setupFrame() {
size = (aux1Bar.getValue()+1)/100.;
turns = (aux3Bar.getValue()/4)+1;
height = (aux2Bar.getValue()+1)/25.;
}
void getField(double result[], double y[]) {
int i, j, n;
result[0] = result[1] = result[2] = 0;
int angct = 8; // was 10 aux3Bar.getValue()+1;
if (turns < 9)
angct = 80/turns;
double ang0 = java.lang.Math.atan2(y[1], y[0]);
double zcoilstep = height/turns;
double zangstep = zcoilstep/angct;
double zbase = -height/2;
double q = .003/(turns*angct);
double lzz = q*zangstep;
if (ang0 < 0)
ang0 += 2*pi;
if (ang0 < 0)
System.out.print("-ang0?? " + ang0 + "\n");
ang0 %= zangstep;
zbase += zcoilstep*ang0/(2*pi);
for (i = 0; i != angct; i++) {
double ang = pi*2*i/angct;
double jxx = size*java.lang.Math.cos(ang+ang0);
double jyy = size*java.lang.Math.sin(ang+ang0);
double jzz = zbase+zangstep*i;
double lxx = -jyy*q;
double lyy = jxx*q;
double rx = y[0]-jxx;
double ry = y[1]-jyy;
double rx2ry2 = rx*rx+ry*ry;
for (j = 0; j != turns; j++) {
double rz = y[2]-jzz;
double r = java.lang.Math.sqrt(rx2ry2+rz*rz);
if (!showA) {
if (r < .04 && useMagnetMove())
boundCheck = true;
// dl x R = (lxx, lyy, lzz) X R
// = (lyy*rz-lzz*ry, lzz*rx-lxx*rz,
// lxx*ry-lyy*rx)
// we are integrating by dl x Rhat/r^2, so we rewrite
// that as dl x R / r^3.
double r3 = r*r*r;
double cx = (lyy*rz-lzz*ry)/r3;
double cy = (lzz*rx-lxx*rz)/r3;
double cz = (lxx*ry-lyy*rx)/r3;
result[0] += cx;
result[1] += cy;
result[2] += cz;
} else {
result[0] += 6*lxx/r;
result[1] += 6*lyy/r;
result[2] += 6*lzz/r;
}
jzz += zcoilstep;
}
}
}
void setup() {
setupBar(0, "Diameter", 40);
setupBar(1, "Height", 30);
setupBar(2, "# of Turns", 36);
setXZView();
}
int getViewPri(double cameraPos[], double x[]) {
return intersectCylinder(cameraPos, x[0], x[1], x[2], 2, false);
}
boolean checkBounds(double y[], double oldY[]) {
if (!useMagnetMove())
return false;
double height2 = height*2;
double r = java.lang.Math.sqrt(y[0]*y[0]+y[1]*y[1]);
double or = java.lang.Math.sqrt(oldY[0]*oldY[0]+oldY[1]*oldY[1]);
// going through walls?
if (y[2] < height2 && y[2] > -height2) {
if ((r < size && or > size) ||
(or < size && r > size))
return true;
}
// passing through z=0 plane inside solenoid?
if ((y[2] > 0 && oldY[2] < 0) ||
(y[2] < 0 && oldY[2] > 0)) {
if (r < size)
return true;
}
return false;
}
void render(Graphics g) {
renderItems(g, 2);
// draw particles inside cylinder; XXX
renderItems(g, 1);
g.setColor(darkYellow);
int i, j;
int angct = 48;
if (turns < 10)
angct = 480/turns;
double zcoilstep = height/turns;
double zangstep = zcoilstep/angct;
double zbase = -height/2;
for (i = 0; i != angct; i++) {
double ang1 = pi*2*i/angct;
double ang2 = pi*2*(i+1)/angct;
double jxx1 = size*java.lang.Math.cos(ang1);
double jyy1 = size*java.lang.Math.sin(ang1);
double jxx2 = size*java.lang.Math.cos(ang2);
double jyy2 = size*java.lang.Math.sin(ang2);
double jzz1 = zbase + zangstep*i;
for (j = 0; j != turns; j++) {
double jzz2 = jzz1 + zangstep;
map3d(jxx1, jyy1, jzz1, xpoints, ypoints, 0);
map3d(jxx2, jyy2, jzz2, xpoints, ypoints, 1);
g.setColor(getCurrentColor(j*angct+i));
if (i == 0 && j == turns/2)
drawCurrentArrow(g, xpoints[0], ypoints[0],
xpoints[1], ypoints[1]);
else
g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
jzz1 += zcoilstep;
}
}
renderItems(g, 0);
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new ToroidalSolenoidField(); }
};
class ToroidalSolenoidField extends VecFunction {
ToroidalSolenoidField() { turnmult = 1; }
String getName() { return "toroidal solenoid"; }
boolean useRungeKutta() { return false; }
double size1, size2, q;
int turns, angct = 8;
int turnmult;
double costab1[], sintab1[];
double costab2[][], sintab2[][];
void setupFrame() {
size1 = aux1Bar.getValue()/100.;
size2 = aux2Bar.getValue()*size1/100.;
turns = aux3Bar.getValue()/3+6;
q = .0003/(angct*turns);
costab1 = new double[angct];
sintab1 = new double[angct];
costab2 = new double[angct][turns];
sintab2 = new double[angct][turns];
int i, j;
for (i = 0; i != angct; i++) {
double ang = pi*2*i/angct;
costab1[i] = java.lang.Math.cos(ang);
sintab1[i] = java.lang.Math.sin(ang);
for (j = 0; j != turns; j++) {
double ang2 = (pi*2*j+ang)/(turnmult*turns);
costab2[i][j] = java.lang.Math.cos(ang2);
sintab2[i][j] = java.lang.Math.sin(ang2);
}
}
}
void finishFrame() {
costab1 = sintab1 = null;
costab2 = sintab2 = null;
}
void setup() {
setupBar(0, "Center Radius", 60);
setupBar(1, "Outer Radius", 80);
setupBar(2, "# of turns", 18);
}
void getField(double result[], double y[]) {
int i, j, n;
result[0] = result[1] = result[2] = 0;
for (i = 0; i != angct; i++) {
double cosp = costab1[i];
double sinp = sintab1[i];
double jzz = size2*sinp;
double lzz = q*turns*size2*cosp;
double rz = y[2]-jzz;
for (j = 0; j != turns; j++) {
double cosa = costab2[i][j];
double sina = sintab2[i][j];
double jxx = cosa*(size1+size2*cosp);
double jyy = sina*(size1+size2*cosp);
double lxx = q*
(-(size1+size2*cosp)*sina - turns*size2*cosa*sinp);
double lyy = q*
((size1+size2*cosp)*cosa - turns*size2*sina*sinp);
double rx = y[0]-jxx;
double ry = y[1]-jyy;
double r = distance(rx, ry, rz);
if (!showA) {
double r3 = r*r*r;
if (r < .04 && useMagnetMove())
boundCheck = true;
// dl x R = (lxx, lyy, lzz) X R
// = (lyy*rz-lzz*ry, lzz*rx-lxx*rz,
// lxx*ry-lyy*rx)
// we are integrating by dl x Rhat/r^2, so we rewrite
// that as dl x R / r^3.
double cx = (lyy*rz-lzz*ry)/r3;
double cy = (lzz*rx-lxx*rz)/r3;
double cz = (lxx*ry-lyy*rx)/r3;
result[0] += cx;
result[1] += cy;
result[2] += cz;
} else {
result[0] += 6*lxx/r;
result[1] += 6*lyy/r;
result[2] += 6*lzz/r;
}
}
}
}
int getViewPri(double cameraPos[], double x[]) {
return intersectCylinder(cameraPos, x[0], x[1], x[2], 2, false);
}
void render(Graphics g) {
renderItems(g, 2);
// draw particles inside cylinder; XXX
renderItems(g, 1);
g.setColor(darkYellow);
int jzz, i;
int steps = turns*48;
for (i = 0; i != steps; i++) {
getToroidPoint(xpoints, ypoints, size1, size2, turns, i, 0);
getToroidPoint(xpoints, ypoints, size1, size2, turns, i+1, 1);
g.setColor(getCurrentColor(i));
if (i == 50)
drawArrow(g, null, xpoints[0], ypoints[0],
xpoints[1], ypoints[1], 7);
else
g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
}
renderItems(g, 0);
}
void getToroidPoint(int xpoints[], int ypoints[], double size1,
double size2, int turns, int i, int n) {
int angct = 48;
double ang = pi*2*(i % angct)/angct;
double cosp = java.lang.Math.cos(ang);
double sinp = java.lang.Math.sin(ang);
double ang2 = (pi*2*(i/angct)+ang)/(turns*turnmult);
double cosa = java.lang.Math.cos(ang2);
double sina = java.lang.Math.sin(ang2);
map3d(cosa*(size1+size2*cosp),
sina*(size1+size2*cosp),
size2*sinp,
xpoints, ypoints, n);
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new HorseshoeElectromagnetField(); }
};
class HorseshoeElectromagnetField extends ToroidalSolenoidField {
HorseshoeElectromagnetField() { turnmult = 2; }
String getName() { return "horseshoe electromagnet"; }
void setup() {
setupBar(0, "Center Radius", 40);
setupBar(1, "Outer Radius", 50);
setupBar(2, "# of turns", 18);
setXYView();
}
VecFunction createNext() { return new SquareLoopField(); }
};
class SquareLoopField extends VecFunction {
String getName() { return "square loop"; }
double lstart, lstop, size;
void setup() {
setupBar(0, "Loop Size", 60);
setXZView();
}
void setupFrame() {
size = aux1Bar.getValue()/100.;
lstart = -size;
lstop = size;
}
void getField(double result[], double y[]) {
getLoopField(result, y, 0, 1);
}
void getLineField(double result[], double y[], double offo,
double offt, int lcoord, int ocoord, int tcoord,
int dir) {
double a1 = lstart-y[lcoord];
double a2 = lstop-y[lcoord];
double r = distance(y[ocoord]-offo, y[tcoord]-offt);
if (r < lineWidth && a1 <= 0 && a2 >= 0)
boundCheck = true;
double y2 = r*r;
double a12 = a1*a1;
double a22 = a2*a2;
double a12y2 = java.lang.Math.sqrt(a12+y2);
double a22y2 = java.lang.Math.sqrt(a22+y2);
if (showA) {
if (lcoord < ocoord)
dir = -dir;
result[lcoord] +=
dir*.0003*java.lang.Math.log((a2+a22y2)/(a1+a12y2))/size;
return;
}
double q = dir*.0001/size;
double fth =
q* (-1/(a12+y2+a1*a12y2)+1/(a22+y2+a2*a22y2));
result[tcoord] += fth*(y[ocoord]-offo);
result[ocoord] -= fth*(y[tcoord]-offt);
}
void getLoopField(double result[], double y[], double zoff, int dir) {
result[0] = result[1] = result[2] = 0;
getLineField(result, y, size, zoff, 0, 1, 2, dir);
getLineField(result, y, -size, zoff, 0, 1, 2, -dir);
getLineField(result, y, size, zoff, 1, 0, 2, dir);
getLineField(result, y, -size, zoff, 1, 0, 2, -dir);
}
boolean checkBounds(double y[], double oldY[]) {
if (!useMagnetMove())
return false;
if ((y[2] > 0 && oldY[2] < 0) ||
(y[2] < 0 && oldY[2] > 0)) {
if (y[0] < size && y[1] < size &&
y[0] > -size && y[1] > -size)
return true;
}
return false;
}
void render(Graphics g) {
renderItems(g, 0);
g.setColor(darkYellow);
map3d(-size, -size, 0, xpoints, ypoints, 0);
map3d(+size, -size, 0, xpoints, ypoints, 1);
map3d(+size, +size, 0, xpoints, ypoints, 2);
map3d(-size, +size, 0, xpoints, ypoints, 3);
int i;
for (i = 0; i != 4; i++) {
int j = (i + 1) & 3;
drawCurrentLine(g, xpoints[i], ypoints[i],
xpoints[j], ypoints[j], 8, i == 0, 1);
}
renderItems(g, 1);
}
int getViewPri(double cameraPos[], double x[]) {
if (intersectZPlane(cameraPos, 0, x[0], x[1], x[2]) == 0)
return 1;
return 0;
}
boolean noSplitFieldVectors() { return false; }
boolean nonGradient() { return true; }
VecFunction createNext() { return new RectLoopField(); }
};
class RectLoopField extends SquareLoopField {
String getName() { return "rectangular loop"; }
double sizeX, sizeY;
void setup() {
setupBar(0, "Loop Width", 60);
setupBar(1, "Loop Depth", 40);
setXZView();
}
void setupFrame() {
sizeX = aux1Bar.getValue()/100.+.01;
sizeY = aux2Bar.getValue()/100.+.01;
}
void getField(double result[], double y[]) {
result[0] = result[1] = result[2] = 0;
lstart = -sizeX;
lstop = sizeX;
size = sizeY;
getLineField(result, y, sizeY, 0, 0, 1, 2, 1);
getLineField(result, y, -sizeY, 0, 0, 1, 2, -1);
lstart = -sizeY;
lstop = sizeY;
size = sizeX;
getLineField(result, y, sizeX, 0, 1, 0, 2, 1);
getLineField(result, y, -sizeX, 0, 1, 0, 2, -1);
}
boolean checkBounds(double y[], double oldY[]) {
if (!useMagnetMove())
return false;
if ((y[2] > 0 && oldY[2] < 0) ||
(y[2] < 0 && oldY[2] > 0)) {
if (y[0] < sizeX && y[1] < sizeY &&
y[0] > -sizeX && y[1] > -sizeY)
return true;
}
return false;
}
void render(Graphics g) {
renderItems(g, 0);
g.setColor(darkYellow);
map3d(-sizeX, -sizeY, 0, xpoints, ypoints, 0);
map3d(+sizeX, -sizeY, 0, xpoints, ypoints, 1);
map3d(+sizeX, +sizeY, 0, xpoints, ypoints, 2);
map3d(-sizeX, +sizeY, 0, xpoints, ypoints, 3);
int i;
for (i = 0; i != 4; i++) {
int j = (i + 1) & 3;
drawCurrentLine(g, xpoints[i], ypoints[i],
xpoints[j], ypoints[j], 8, i == 0, 1);
}
renderItems(g, 1);
}
VecFunction createNext() { return new CornerField(); }
};
class CornerField extends SquareLoopField {
String getName() { return "corner"; }
void setup() {
setXZView();
setupBar(0, "Offset", 50);
}
double offset;
void setupFrame() {
size = 2;
offset = aux1Bar.getValue()/50.-1;
lstart = offset;
lstop = 10+offset;
}
void getField(double result[], double y[]) {
result[0] = result[1] = result[2] = 0;
getLineField(result, y, offset, 0, 0, 1, 2, -1);
getLineField(result, y, offset, 0, 1, 0, 2, -1);
}
void render(Graphics g) {
renderItems(g, 0);
g.setColor(darkYellow);
map3d(offset, offset, 0, xpoints, ypoints, 0);
map3d(1, offset, 0, xpoints, ypoints, 1);
map3d(offset, 1, 0, xpoints, ypoints, 2);
drawCurrentLine(g,
xpoints[0], ypoints[0],
xpoints[1], ypoints[1], 8, true, 1);
drawCurrentLine(g,
xpoints[2], ypoints[2],
xpoints[0], ypoints[0], 8, false, 1);
renderItems(g, 1);
}
VecFunction createNext() { return new MagneticSphereB(); }
};
class MagneticSphereB extends VecFunction {
String getName() { return "magnetic sphere"; }
void getField(double result[], double y[]) {
double a = aux1Bar.getValue()/100.;
double r = distance(y);
if (r < a) {
boundCheck = true;
result[0] = result[1] = result[2] = 0;
return;
}
double rz = distance(y[0], y[1]);
double costh = y[2]/r;
double sinth = rz/r;
double sinph = y[1]/rz;
double cosph = y[0]/rz;
if (!showA) {
// rhat = (sinth*cosph, sinth*sinph, costh)
// thhat = (costh*cosph, costh*sinph, -sinth)
double r3 = .003*a*a*a/(r*r*r);
double eth = 2*sinth*r3;
double er = costh*r3;
result[0] = sinth*cosph*er + costh*cosph*eth;
result[1] = sinth*sinph*er + costh*sinph*eth;
result[2] = costh *er - sinth* eth;
} else {
// phhat = (-sinph, cosph, 0)
double aph = .003*a*a*a*sinth/(r*r);
result[0] = -sinph*aph;
result[1] = cosph*aph;
result[2] = 0;
}
}
void setup() {
setupBar(0, "Sphere Size", 50);
setXZView();
}
void render(Graphics g) {
double a = aux1Bar.getValue()/100.;
fillSphere(g, a, 0);
renderItems(g, 0);
}
int getViewPri(double cameraPos[], double x[]) {
double a = aux1Bar.getValue()/100.;
return intersectSphere(cameraPos, x[0], x[1], x[2], a);
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new MonopoleAttempt(); }
};
class MonopoleAttempt extends SquareLoopField {
String getName() { return "monopole attempt"; }
double tres[][], yflip[], rad, size;
int count;
MonopoleAttempt() {
tres = new double[8][3];
yflip = new double[3];
}
void setup() {
setXZView();
setupBar(0, "Loop Size", 40);
setupBar(1, "Separation", 10);
setupBar(2, "Loop Count", 100);
dispChooser.select(DISP_VECTORS);
}
void setupFrame() {
super.setupFrame();
size = (aux1Bar.getValue())/100.;
rad = aux2Bar.getValue()/100. + size;
count = (aux3Bar.getValue()*6)/101 + 1;
}
void drawLoop(Graphics g) {
int i;
for (i = 0; i != 4; i++) {
int j = (i + 1) & 3;
drawCurrentLine(g, xpoints[i], ypoints[i],
xpoints[j], ypoints[j], 8, i == 0, 1);
}
}
void render(Graphics g) {
renderItems(g, 0);
g.setColor(darkYellow);
double size = aux1Bar.getValue()/100.;
int i;
int ct = count;
for (i = -1; i <= 1; i += 2) {
if (--ct < 0)
break;
map3d(-size, -size, rad*i, xpoints, ypoints, 0);
map3d(+size*i, -size*i, rad*i, xpoints, ypoints, 1);
map3d(+size, +size, rad*i, xpoints, ypoints, 2);
map3d(-size*i, +size*i, rad*i, xpoints, ypoints, 3);
drawLoop(g);
}
for (i = -1; i <= 1; i += 2) {
if (--ct < 0)
break;
map3d(-size, rad*i, -size, xpoints, ypoints, 0);
map3d(-size*i, rad*i, +size*i, xpoints, ypoints, 1);
map3d(+size, rad*i, +size, xpoints, ypoints, 2);
map3d(+size*i, rad*i, -size*i, xpoints, ypoints, 3);
drawLoop(g);
}
for (i = -1; i <= 1; i += 2) {
if (--ct < 0)
break;
map3d(rad*i, -size, -size, xpoints, ypoints, 0);
map3d(rad*i, +size*i, -size*i, xpoints, ypoints, 1);
map3d(rad*i, +size, +size, xpoints, ypoints, 2);
map3d(rad*i, -size*i, +size*i, xpoints, ypoints, 3);
drawLoop(g);
}
renderItems(g, 1);
}
void getField(double result[], double y[]) {
int i;
for (i = 0; i != 6; i++)
tres[i][0] = tres[i][1] = tres[i][2] = 0;
getLoopField(tres[0], y, -rad, -1);
if (count > 1)
getLoopField(tres[1], y, rad, 1);
yflip[1] = y[0];
yflip[2] = y[1];
yflip[0] = y[2];
if (count > 2)
getLoopField(tres[2], yflip, -rad, -1);
if (count > 3)
getLoopField(tres[3], yflip, rad, 1);
yflip[2] = y[0];
yflip[0] = y[1];
yflip[1] = y[2];
if (count > 4)
getLoopField(tres[4], yflip, -rad, -1);
if (count > 5)
getLoopField(tres[5], yflip, rad, 1);
for (i = 0; i != 3; i++)
result[i] = tres[0][i] + tres[1][i] +
tres[2][(i+1)%3] + tres[3][(i+1)%3] +
tres[4][(i+2)%3] + tres[5][(i+2)%3];
}
VecFunction createNext() { return null; }
};
#endif
#ifdef BUILD_V
class InverseSquaredRadialSphere extends VecFunction {
String getName() { return "1/r^2 sphere"; }
double getSize() { return (aux1Bar.getValue()+1)/110.; }
void getField(double result[], double y[]) {
double r = distance(y);
if (r < .01)
boundCheck = true;
double a = getSize();
if (getPot) {
result[0] = .1*((r > a) ? -1/r : -3/(2*a)+r*r/(2*a*a*a));
return;
}
if (r < a)
r = a;
double alpha = .0003/(r*r*r);
result[0] = -y[0]*alpha;
result[1] = -y[1]*alpha;
result[2] = -y[2]*alpha;
}
void setup() {
setupBar(0, "Sphere Size", 70);
}
void render(Graphics g) {
renderSphere(g, getSize());
}
int getViewPri(double cameraPos[], double x[]) {
return intersectSphere(cameraPos, x[0], x[1], x[2], getSize());
}
boolean noSplitFieldVectors() { return false; }
boolean checkBoundsWithForce() { return false; }
VecFunction createNext() { return new ConstRadial(); }
};
class ConstRadial extends InverseSquaredRadial {
String getName() { return "const radial"; }
void getField(double result[], double y[]) {
double r = distance(y);
if (r < chargeSize)
boundCheck = true;
double q = .0003/r;
if (getPot) {
result[0] = r-1;
return;
}
result[0] = -q*y[0];
result[1] = -q*y[1];
result[2] = -q*y[2];
}
boolean checkBoundsWithForce() { return false; }
VecFunction createNext() { return new LinearRadial(); }
};
class LinearRadial extends InverseSquaredRadial {
String getName() { return "linear radial"; }
void getField(double result[], double y[]) {
double r = distance(y);
if (r < chargeSize)
boundCheck = true;
if (getPot) {
result[0] = r*r-1;
return;
}
double k = .0003;
result[0] = -y[0]*k;
result[1] = -y[1]*k;
result[2] = -y[2]*k;
}
boolean checkBoundsWithForce() { return false; }
VecFunction createNext() { return new ConstantToZAxis(); }
};
class ConstantToZAxis extends InverseRadial {
String getName() { return "constant to z axis"; }
void getField(double result[], double y[]) {
double r = distance(y[0], y[1]);
if (r < lineWidth)
boundCheck = true;
if (getPot) {
result[0] = r-1;
return;
}
double q = .0003/r;
result[0] = -y[0]*q;
result[1] = -y[1]*q;
result[2] = 0;
}
void setup() {
setXYView();
}
boolean checkBoundsWithForce() { return false; }
VecFunction createNext() { return new ConstantToXYPlane(); }
};
class ConstantToXYPlane extends VecFunction {
String getName() { return "constant to xy plane"; }
void getField(double result[], double y[]) {
double alpha = .0003;
if (y[2] > -.01 && y[2] < .01)
boundCheck = true;
if (getPot) {
result[0] = java.lang.Math.abs(y[2])-1;
return;
}
result[0] = 0;
result[1] = 0;
result[2] = (y[2] < 0) ? alpha : -alpha;
}
void setup() {
setXZView();
}
void render(Graphics g) {
renderItems(g, 1);
drawPlane(g, 1, 1, 0);
renderItems(g, 0);
}
int getViewPri(double cameraPos[], double x[]) {
if (intersectZPlane(cameraPos, 0, x[0], x[1], x[2]) == 0)
return 0;
return 1;
}
boolean checkBoundsWithForce() { return false; }
VecFunction createNext() { return new LinearToZAxis(); }
};
class LinearToZAxis extends InverseRadial {
String getName() { return "linear to z axis"; }
void getField(double result[], double y[]) {
double r = distance(y[0], y[1]);
if (r < lineWidth)
boundCheck = true;
if (getPot) {
result[0] = r*r-1;
return;
}
double q = .0003;
result[0] = -y[0]*q;
result[1] = -y[1]*q;
result[2] = 0;
}
void setup() {
setXYView();
}
boolean checkBoundsWithForce() { return false; }
VecFunction createNext() { return new LinearToXYPlane(); }
};
class LinearToXYPlane extends ConstantToXYPlane {
String getName() { return "linear to xy plane"; }
void getField(double result[], double y[]) {
if (getPot) {
result[0] = y[2]*y[2]-1;
return;
}
if (y[2] > -.01 && y[2] < .01)
boundCheck = true;
double alpha = .0003;
result[0] = 0;
result[1] = 0;
result[2] = -alpha*y[2];
}
void setup() {
setXYView();
}
boolean checkBoundsWithForce() { return false; }
VecFunction createNext() { return new LinearToYZXZPlane(); }
};
class LinearToYZXZPlane extends VecFunction {
String getName() { return "linear to yz, xz planes"; }
void getField(double result[], double y[]) {
if (y[0] > -.01 && y[0] < .01)
boundCheck = true;
if (y[1] > -.01 && y[1] < .01)
boundCheck = true;
double alpha = .0003;
double r = java.lang.Math.sqrt((aux1Bar.getValue()+1)/51.);
if (getPot) {
result[0] = (y[0]*y[0]*r+y[1]*y[1]/r)-1;
return;
}
result[0] = -alpha*r*y[0];
result[1] = -alpha/r*y[1];
result[2] = 0;
}
void setup() {
setXYView();
setupBar(0, "X/Y Ratio", 50);
}
boolean checkBoundsWithForce() { return false; }
VecFunction createNext() { return new LinearToYZXZXYPlane(); }
};
class LinearToYZXZXYPlane extends VecFunction {
String getName() { return "linear to yz, xz, xy planes"; }
void getField(double result[], double y[]) {
if (y[2] > -.01 && y[2] < .01)
boundCheck = true;
double alpha = .0003;
double r1 = (aux1Bar.getValue()+1)/51.;
double r2 = (aux2Bar.getValue()+1)/51.;
if (getPot) {
result[0] = (y[0]*y[0]*r1+y[1]*y[1]+y[2]*y[2]/r2)-1;
return;
}
result[0] = -alpha*r1*y[0];
result[1] = -alpha *y[1];
result[2] = -alpha/r2*y[2];
}
void setup() {
setXYView();
setupBar(0, "X/Y Ratio", 50);
setupBar(1, "Y/Z Ratio", 50);
}
boolean checkBoundsWithForce() { return false; }
VecFunction createNext() { return new InverseToXYPlane(); }
};
class InverseToXYPlane extends ConstantToXYPlane {
String getName() { return "inverse to xy plane"; }
void getField(double result[], double y[]) {
if (y[2] > -.01 && y[2] < .01)
boundCheck = true;
double alpha = .0003;
double zz = y[2];
if (getPot) {
result[0] = -.01/(zz*zz);
return;
}
if (zz == 0)
zz = .00001;
result[0] = 0;
result[1] = 0;
result[2] = -alpha/zz;
}
void setup() {
setXZView();
}
VecFunction createNext() { return new InverseSquareRotational(); }
};
class InverseSquareRotational extends InverseRadial {
String getName() { return "1/r^2 rotational"; }
void getField(double result[], double y[]) {
double r = distance(y[0], y[1]);
if (r < lineWidth*2)
boundCheck = true;
rotateParticle(result, y, .0001/(r*r*r));
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new LinearRotational(); }
};
class LinearRotational extends InverseRadial {
String getName() { return "linear rotational"; }
void setup() {
setXYViewExact();
}
void getField(double result[], double y[]) {
double q = .0003;
result[0] = -q*y[1];
result[1] = q*y[0];
result[2] = 0;
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new LinearRotationalA(); }
};
class LinearRotationalA extends InverseRadial {
String getName() { return "fz=-(x^2+y^2)"; }
void setup() {
setXZView();
}
void getField(double result[], double y[]) {
double q = .0003;
result[0] = result[1] = 0;
result[2] = -q*(y[0]*y[0]+y[1]*y[1]);
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new ConstantRotational(); }
};
class ConstantRotational extends InverseRadial {
String getName() { return "constant rotational"; }
void setup() {
setXYViewExact();
}
void getField(double result[], double y[]) {
double r = distance(y[0], y[1]);
rotateParticle(result, y, .0003/r);
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new ConstantRotationalA(); }
};
class ConstantRotationalA extends InverseRadial {
String getName() { return "(0,0,-r)"; }
void setup() {
setXZView();
}
void getField(double result[], double y[]) {
double r = distance(y[0], y[1]);
result[0] = result[1] = 0;
result[2] = -.0006*r;
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new Helical(); }
};
class Helical extends InverseRadial {
String getName() { return "helical"; }
void setup() {
setXZView();
setupBar(0, "Z Speed", 30);
}
void getField(double result[], double y[]) {
double q= .0003;
result[0] = -q*y[1];
result[1] = q*y[0];
result[2] = .00001*aux1Bar.getValue();
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new FxEqualsYField(); }
};
class FxEqualsYField extends VecFunction {
String getName() { return "fx=y"; }
void getField(double result[], double y[]) {
result[2] = result[1] = 0;
result[0] = y[1] * .0006;
}
void setup() {
setXYView();
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new FxEqualsY2(); }
};
class FxEqualsY2 extends VecFunction {
String getName() { return "fx=y2"; }
void getField(double result[], double y[]) {
result[2] = result[1] = 0;
result[0] = y[1]*y[1] * .001;
}
void setup() {
setXYView();
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new LinearZRotational(); }
};
class LinearZRotational extends VecFunction {
String getName() { return "(-yz,xz,0)"; }
void setup() {
setXZView();
}
void getField(double result[], double y[]) {
double q = .001*y[2];
result[0] = -q*y[1];
result[1] = q*y[0];
result[2] = 0;
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new YzXz0Field(); }
};
class YzXz0Field extends VecFunction {
String getName() { return "(yz,xz,0)"; }
void setup() {
setXZView();
}
void getField(double result[], double y[]) {
double q = .0006*y[2];
result[0] = q*y[1];
result[1] = q*y[0];
result[2] = 0;
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new XY_2ZField(); }
};
class XY_2ZField extends VecFunction {
String getName() { return "(-x,-y,2z)"; }
void setup() {
setXZView();
}
void getField(double result[], double y[]) {
double q = .0006;
result[0] = q*y[0];
result[1] = q*y[1];
result[2] = -2*q*y[2];
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new XY0Field(); }
};
class XY0Field extends VecFunction {
String getName() { return "(-x,y,0)"; }
void setup() {
setXYView();
}
void getField(double result[], double y[]) {
double q = .0006;
result[0] = -q*y[0];
result[1] = q*y[1];
result[2] = 0;
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new RotationalExpansion(); }
};
class RotationalExpansion extends VecFunction {
String getName() { return "(x-y,x+y,0)"; }
void getField(double result[], double y[]) {
double q = .0003;
result[0] = q*(y[0]-y[1]);
result[1] = q*(y[0]+y[1]);
result[2] = 0;
}
void setup() {
setXYView();
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new RotationalExpansion3D(); }
};
class RotationalExpansion3D extends VecFunction {
String getName() { return "(x-y,x+y,2z)"; }
void getField(double result[], double y[]) {
double q = .0003;
result[0] = q*(y[0]-y[1]);
result[1] = q*(y[0]+y[1]);
result[2] = q*(y[2]*2);
}
void setup() {
setXYView();
}
boolean nonGradient() { return true; }
VecFunction createNext() { return new RosslerAttractor(); }
};
class RosslerAttractor extends VecFunction {
String getName() { return "Rossler attractor"; }
void getField(double result[], double y[]) {
int scale = aux2Bar.getValue()*2+20;
double xx = y[0] * 24;
double yy = y[1] * 24;
double zz = (y[2]+.75) * scale;
double k = .00002;
double c = aux1Bar.getValue()*.1;
result[0] = -(yy+zz)*k;
result[1] = k*(xx+.2*yy);
result[2] = k*(.2+xx*zz-c*zz);
}
void setup() {
setXZView();
setupBar(0, "c", 80);
setupBar(1, "Z Scale", 36);
strengthBar.setValue(75);
}
boolean nonGradient() { return true; }
boolean redistribute() { return false; }
VecFunction createNext() { return new LorenzAttractor(); }
};
class LorenzAttractor extends VecFunction {
String getName() { return "Lorenz attractor"; }
void setup() {
setXZView();
setupBar(0, "Scale", 24);
}
void getField(double result[], double y[]) {
int scale = aux1Bar.getValue()/2 + 23;
double xx = y[0] * scale;
double yy = y[1] * scale;
double zz = y[2] * scale + scale;
double k = .00002;
result[0] = (-10*xx+10*yy)*k;
result[1] = k*(28*xx-yy-xx*zz);
result[2] = k*(-(8./3.)*zz+xx*yy);
}
boolean nonGradient() { return true; }
boolean redistribute() { return false; }
VecFunction createNext() { return new UserDefinedPotential(); }
};
class UserDefinedPotential extends VecFunction {
Expr expr;
double y0[];
String getName() { return "user-defined potential"; }
void setup() {
setXZView();
textFields[0].setText("x*x-z*z");
textFields[0].show();
textFieldLabel.setText("Potential Function");
textFieldLabel.show();
actionPerformed();
y0 = new double[3];
}
void actionPerformed() {
parseError = false;
ExprParser ep = new ExprParser(textFields[0].getText());
expr = ep.parseExpression();
if (ep.gotError())
parseError = true;
}
void getField(double result[], double y[]) {
double k = .00001;
int i;
for (i = 0; i != 3; i++)
y0[i] = y[i];
double pot0 = expr.eval(y0);
if (getPot) {
result[0] = pot0*.01;
return;
}
y0[0] += k;
result[0] = pot0-expr.eval(y0);
y0[0] = y[0];
y0[1] += k;
result[1] = pot0-expr.eval(y0);
y0[1] = y[1];
y0[2] += k;
result[2] = pot0-expr.eval(y0);
for (i = 0; i != 3; i++)
if (!(result[i] > -10 && result[i] < 10))
boundCheck = true;
}
VecFunction createNext() { return new UserDefinedFunction(); }
};
class UserDefinedFunction extends VecFunction {
Expr exprs[];
String getName() { return "user-defined field"; }
void setup() {
setXZView();
exprs = new Expr[3];
textFields[0].setText("x");
textFields[1].setText("y");
textFields[2].setText("z");
textFieldLabel.setText("Field Functions");
textFieldLabel.show();
int i;
for (i = 0; i != 3; i++)
textFields[i].show();
actionPerformed();
}
void actionPerformed() {
int i;
parseError = false;
for (i = 0; i != 3; i++) {
ExprParser ep = new ExprParser(textFields[i].getText());
exprs[i] = ep.parseExpression();
if (ep.gotError())
parseError = true;
}
}
void getField(double result[], double y[]) {
double k = .0002;
int i;
for (i = 0; i != 3; i++) {
result[i] = k*exprs[i].eval(y);
if (!(result[i] > -10 && result[i] < 10))
boundCheck = true;
}
}
boolean nonGradient() { return true; }
VecFunction createNext() { return null; }
};
#endif
class DrawData {
public Graphics g;
public double mult;
public double field[], vv[];
};
class Particle {
public double pos[];
public double vel[];
public int viewPri;
public double lifetime;
public double phi, theta, phiv, thetav;
public double stepsize;
Particle() {
pos = new double[3]; vel = new double[3];
stepsize = 1;
}
};
class FieldVector {
public int sx1, sy1, sx2, sy2;
public double p1[], p2[];
public int col;
public int viewPri;
};
#ifdef BUILD_E
// this is a complex number class that avoids creating new objects
// whenever possible. Takes a little more effort to use, but it is
// much faster because we don't have to do constant garbage
// collection.
class Complex {
public double a, b;
Complex() { a = b = 0; }
void set(double aa, double bb) { a = aa; b = bb; }
void set(Complex c) { set(c.a, c.b); }
void add(double r) { a += r; }
void add(double r, double i) { a += r; b += i; }
void square() { set(a*a-b*b, 2*a*b); }
void mult(double c, double d) { set(a*c-b*d, a*d+b*c); }
void mult(double c) { a *= c; b *= c; }
void mult(Complex c) { mult(c.a, c.b); }
void recip() {
double n = a*a+b*b;
set(a/n, -b/n);
}
void pow(double p) {
double arg = java.lang.Math.atan2(b, a);
arg *= p;
double abs = java.lang.Math.pow(a*a+b*b, p*.5);
set(abs*java.lang.Math.cos(arg),
abs*java.lang.Math.sin(arg));
}
void sin() {
set(cosh(b)*java.lang.Math.sin(a),
java.lang.Math.cos(a)*sinh(b));
}
void cos() {
set(cosh(b)*java.lang.Math.cos(a),
java.lang.Math.sin(a)*sinh(b));
}
void log() {
set(java.lang.Math.log(a*a+b*b),
java.lang.Math.atan2(b, a));
}
void arcsin() {
Complex z2 = new Complex();
z2.set(a, b);
z2.square();
z2.mult(-1);
z2.add(1);
z2.pow(.5);
mult(0, 1);
add(z2.a, z2.b);
log();
mult(0, -1);
}
};
double cosh(double a) {
return .5*(java.lang.Math.exp(a)+java.lang.Math.exp(-a));
}
double sinh(double a) {
return .5*(java.lang.Math.exp(a)-java.lang.Math.exp(-a));
}
#endif
};
#ifdef BUILD_V
class ExprState {
public double x, y, z;
}
class Expr {
Expr(Expr e1, Expr e2, int v) {
left = e1;
right = e2;
type = v;
}
Expr(int v, double vv) {
type = v;
value = vv;
}
Expr(int v) {
type = v;
}
double eval(double es[]) {
switch (type) {
case E_ADD: return left.eval(es)+right.eval(es);
case E_SUB: return left.eval(es)-right.eval(es);
case E_MUL: return left.eval(es)*right.eval(es);
case E_DIV: return left.eval(es)/right.eval(es);
case E_POW: return java.lang.Math.pow(left.eval(es), right.eval(es));
case E_UMINUS: return -left.eval(es);
case E_VAL: return value;
case E_X: return es[0]*10;
case E_Y: return es[1]*10;
case E_Z: return es[2]*10;
case E_R: return java.lang.Math.sqrt(
es[0]*es[0]+es[1]*es[1]+es[2]*es[2])*10;
case E_SIN: return java.lang.Math.sin(left.eval(es));
case E_COS: return java.lang.Math.cos(left.eval(es));
case E_ABS: return java.lang.Math.abs(left.eval(es));
case E_EXP: return java.lang.Math.exp(left.eval(es));
case E_LOG: return java.lang.Math.log(left.eval(es));
case E_SQRT: return java.lang.Math.sqrt(left.eval(es));
case E_TAN: return java.lang.Math.tan(left.eval(es));
default: System.out.print("unknown\n");
}
return 0;
}
Expr left, right;
double value;
int type;
static final int E_ADD = 1;
static final int E_SUB = 2;
static final int E_X = 3;
static final int E_Y = 4;
static final int E_Z = 5;
static final int E_VAL = 6;
static final int E_MUL = 7;
static final int E_DIV = 8;
static final int E_POW = 9;
static final int E_UMINUS = 10;
static final int E_SIN = 11;
static final int E_COS = 12;
static final int E_ABS = 13;
static final int E_EXP = 14;
static final int E_LOG = 15;
static final int E_SQRT = 16;
static final int E_TAN = 17;
static final int E_R = 18;
};
class ExprParser {
String text;
String token;
int pos;
int tlen;
boolean err;
void getToken() {
while (pos < tlen && text.charAt(pos) == ' ')
pos++;
if (pos == tlen) {
token = "";
return;
}
int i = pos;
int c = text.charAt(i);
if ((c >= '0' && c <= '9') || c == '.') {
for (i = pos; i != tlen; i++) {
if (!((text.charAt(i) >= '0' && text.charAt(i) <= '9') ||
text.charAt(i) == '.'))
break;
}
} else if (c >= 'a' && c <= 'z') {
for (i = pos; i != tlen; i++) {
if (!(text.charAt(i) >= 'a' && text.charAt(i) <= 'z'))
break;
}
} else {
i++;
}
token = text.substring(pos, i);
pos = i;
}
boolean skip(String s) {
if (token.compareTo(s) != 0)
return false;
getToken();
return true;
}
void skipOrError(String s) {
if (!skip(s))
err = true;
}
Expr parseExpression() {
if (token.length() == 0)
return new Expr(Expr.E_VAL, 0.);
Expr e = parse();
if (token.length() > 0)
err = true;
return e;
}
Expr parse() {
Expr e = parseMult();
while (true) {
if (skip("+"))
e = new Expr(e, parseMult(), Expr.E_ADD);
else if (skip("-"))
e = new Expr(e, parseMult(), Expr.E_SUB);
else
break;
}
return e;
}
Expr parseMult() {
Expr e = parseUminus();
while (true) {
if (skip("*"))
e = new Expr(e, parseUminus(), Expr.E_MUL);
else if (skip("/"))
e = new Expr(e, parseUminus(), Expr.E_DIV);
else
break;
}
return e;
}
Expr parseUminus() {
skip("+");
if (skip("-"))
return new Expr(parsePow(), null, Expr.E_UMINUS);
return parsePow();
}
Expr parsePow() {
Expr e = parseTerm();
while (true) {
if (skip("^"))
e = new Expr(e, parseTerm(), Expr.E_POW);
else
break;
}
return e;
}
Expr parseFunc(int t) {
skipOrError("(");
Expr e = parse();
skipOrError(")");
return new Expr(e, null, t);
}
Expr parseTerm() {
if (skip("(")) {
Expr e = parse();
skipOrError(")");
return e;
}
if (skip("x"))
return new Expr(Expr.E_X);
if (skip("y"))
return new Expr(Expr.E_Y);
if (skip("z"))
return new Expr(Expr.E_Z);
if (skip("r"))
return new Expr(Expr.E_R);
if (skip("pi"))
return new Expr(Expr.E_VAL, 3.14159265358979323846);
if (skip("e"))
return new Expr(Expr.E_VAL, 2.7182818284590452354);
if (skip("sin"))
return parseFunc(Expr.E_SIN);
if (skip("cos"))
return parseFunc(Expr.E_COS);
if (skip("abs"))
return parseFunc(Expr.E_ABS);
if (skip("exp"))
return parseFunc(Expr.E_EXP);
if (skip("log"))
return parseFunc(Expr.E_LOG);
if (skip("sqrt"))
return parseFunc(Expr.E_SQRT);
if (skip("tan"))
return parseFunc(Expr.E_TAN);
try {
Expr e = new Expr(Expr.E_VAL, Double.valueOf(token).doubleValue());
getToken();
return e;
} catch (Exception e) {
err = true;
System.out.print("unrecognized token: " + token + "\n");
return new Expr(Expr.E_VAL, 0);
}
}
ExprParser(String s) {
text = s;
tlen = text.length();
pos = 0;
err = false;
getToken();
}
boolean gotError() { return err; }
};
#endif
A far higher place must be assigned to Judaism among the competitors for the allegiance of Europe. The cosmopolitan importance at one time assumed by this religion has been considerably obscured, owing to the subsequent devolution of its part to Christianity. It is, however, by no means impossible that, but for the diversion created by the Gospel, and the disastrous consequences of their revolt against Rome, the Jews might have won the world to a purified form of their own monotheism. A few significant circumstances are recorded showing how much influence they had acquired, even in Rome, before the first preaching of Christianity. The first of these is to be found in Cicero¡¯s defence of Flaccus. The latter was accused of appropriating part of the annual contributions sent to the temple at Jerusalem; and, in dealing with this charge, Cicero speaks of the Jews, who were naturally prejudiced against his client, as a powerful faction the hostility of which he is anxious not to provoke.330 Some twenty years later, a great advance has been made. Not only must the material interests of the Jews be respected, but a certain conformity to their religious prescriptions is considered a mark of good breeding, In one of his most amusing satires, Horace tells us how, being anxious to shake off a bore, he appeals for help to his friend Aristius Fuscus, and reminds him of217 some private business which they had to discuss together. Fuscus sees his object, and being mischievously determined to defeat it, answers: ¡®Yes, I remember perfectly, but we must wait for some better opportunity; this is the thirtieth Sabbath, do you wish to insult the circumcised Jews?¡¯ ¡®I have no scruples on that point,¡® replies the impatient poet. ¡®But I have,¡¯ rejoins Fuscus,¡ª¡®a little weak-minded, one of the many, you know¡ªexcuse me, another time.¡®331 Nor were the Jews content with the countenance thus freely accorded them. The same poet elsewhere intimates that whenever they found themselves in a majority, they took advantage of their superior strength to make proselytes by force.¡¯332 And they pursued the good work to such purpose that a couple of generations later we find Seneca bitterly complaining that the vanquished had given laws to the victors, and that the customs of this abominable race were established over the whole earth.333 Evidence to the same effect is given by Philo Judaeus and Josephus, who inform us that the Jewish laws and customs were admired, imitated, and obeyed over the whole earth.334 Such assertions might be suspected of exaggeration, were they not, to a certain extent, confirmed by the references already quoted, to which others of the same kind may be added from later writers showing that it was a common practice among the Romans to abstain from work on the Sabbath, and even to celebrate it by praying, fasting, and lighting lamps, to visit the synagogues, to study the law of Moses, and to pay the yearly contribution of two drachmas to the temple at Jerusalem.335 Jeff¡¯s hand was quietly coming down. ¡°What happened to you?¡± begged Sandy. ¡°Something new has come up, sir. I was waiting there by my ship a good while back, and I heard another one cruising and spiraling, shooting the field, I guess, because he came in and set down. My crate, just the way you ordered, was down by the grove, not in plain sight in the middle of the course. But Jeff set his ship down, left the engine running, and went off. I stayed hid to see what would happen, but when he didn¡¯t come back, I thought I¡¯d better go and find you¡ªand see if it meant anything to you.¡± She stood alone, with the sticky, wet knife in her hand, catching her breath, coming out of the madness. Then she stooped, and pushing the branches aside felt about for her pistol. It lay at the root of a tree, and[Pg 80] when she had picked it up and put it back in the holster, there occurred to her for the first time the thought that the shot in the dead stillness must have roused the camp. And now she was sincerely frightened. If she were found here, it would be more than disagreeable for Landor. They must not find her. She started at a swift, long-limbed run, making a wide detour, to avoid the sentries, bending low, and flying silently among the bushes and across the shadowy sands. The year 1756 opened with menaces to England of the most serious nature. The imbecility of the Ministry was beginning to tell in the neglect of its colonies and its defences. France threatened to invade us, and a navy of fifty thousand men was suddenly voted, and an army of thirty-four thousand two hundred and sixty-three of native troops; but as these were not ready, it was agreed to bring over eight thousand Hessians and Hanoverians. To pay for all this it was necessary to grant excessive supplies, and lay on new duties and taxes. In presenting the money bills in the month of May, Speaker Onslow could not avoid remarking that there were two circumstances which tended to create alarm¡ªforeign subsidies and foreign troops introduced, and nothing but their confidence in his Majesty could allay their fears, or give them confidence that their burdens would be soon reduced. There was, in fact, no chance for any such reduction, for wars, troubles, and disgraces were gathering around from various quarters. The first reverse came from the Mediterranean. MUCH to their amazement, the boys waked up the next morning in Nashville, and found that they had passed through the "dark and bloody ground" of Kentucky absolutely without adventure. After drawing and dividing the rations and cartridges. Si gave the boys the necessary instruction about having their things ready so that they could get them in the dark the next morning, and ordered them to disregard the bonfires and mirth-making, and lie down to get all the sleep they could, in preparation for the hard work of the next day. Then, like the rest of the experienced men, who saw that the campaign was at length really on, and this would be the last opportunity for an indefinite while to write, he sat down to write short letters to his mother and to Annabel. "Bully for the Wild Wanderers of the Wabash," Shorty joined in. "They're the boss regiment in the army o' the Cumberland, and the Army o' the Cumberland's the boss army on earth. Hooray for US Co. Le's have a speech. Where's Monty Scruggs?" "Bring a light, do¡ªI can't abide this dark." Albert suddenly began to look uneasy. After all he was not really drunk, only a little fuddled. He walked straight, and his roll was natural to him, while though he was exceedingly cheerful, and often burst into song, his words were not jumbled, and he generally seemed to have a fair idea of what he was saying. "But I heard what the doctor said to you." "A purty accident¡ªwud them stacks no more dry than a ditch. 'Twas a clear case of 'bustion¡ªfireman said so to me; as wicked and tedious a bit o' wark as ever I met in my life." Calverley stept from the shadow of the cliff, and beheld a meteor in the sky, brightening and expanding, as the clouds opened, until it assumed the appearance of a brilliant star, of astonishing magnitude, encircled by dazzling rays, which, in a singular manner, were all inclined in one direction, and pointing to that part of the horizon where lay the rival of England¡ªFrance. The foreman's face assumed a deeper hue than usual: he looked fiercely at the galleyman, but there was a determination in the weather-beaten face that made him pause ere he spoke. "Galleyman," he at length said, "you knew the business before you came: if you be so fond of saving old witches' lives, why didn't you say so, that I might not now be in this dilemma?" "No, no, not the boy," replied Merritt, rather impatiently. HoMEÁíÀàС˵ߣɫ
ENTER NUMBET 0017
tala2.net.cn
aa6l6.com.cn
yinti6.com.cn
geju5.com.cn
huoxi6.com.cn
erjun0.com.cn
www.koubu7.com.cn
kante2.net.cn
www.zikou6.net.cn
www.166webfind.com.cn