// Wave2d.java (c) 2004 by Paul Falstad, www.falstad.com
import java.io.InputStream;
import java.awt.*;
import java.awt.image.*;
import java.applet.Applet;
import java.util.Vector;
import java.io.File;
import java.util.Random;
import java.util.Arrays;
import java.lang.Math;
import java.text.NumberFormat;
import java.awt.event.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
class Wave2dCanvas extends Canvas {
Wave2dFrame pg;
Wave2dCanvas(Wave2dFrame p) {
pg = p;
}
public Dimension getPreferredSize() {
return new Dimension(300,400);
}
public void update(Graphics g) {
pg.updateWave2d(g);
}
public void paint(Graphics g) {
pg.updateWave2d(g);
}
};
class Wave2dLayout implements LayoutManager {
public Wave2dLayout() {}
public void addLayoutComponent(String name, Component c) {}
public void removeLayoutComponent(Component c) {}
public Dimension preferredLayoutSize(Container target) {
return new Dimension(500, 500);
}
public Dimension minimumLayoutSize(Container target) {
return new Dimension(100,100);
}
public void layoutContainer(Container target) {
Insets insets = target.insets();
int targetw = target.size().width - insets.left - insets.right;
int cw = targetw* 7/10;
int targeth = target.size().height - (insets.top+insets.bottom);
target.getComponent(0).move(insets.left, insets.top);
target.getComponent(0).resize(cw, targeth);
int barwidth = targetw - cw;
cw += insets.left;
int i;
int h = insets.top;
for (i = 1; i < target.getComponentCount(); i++) {
Component m = target.getComponent(i);
if (m.isVisible()) {
Dimension d = m.getPreferredSize();
if (m instanceof Scrollbar)
d.width = barwidth;
if (m instanceof Choice && d.width > barwidth)
d.width = barwidth;
if (m instanceof Label) {
h += d.height/5;
d.width = barwidth;
}
m.move(cw, h);
m.resize(d.width, d.height);
h += d.height;
}
}
}
};
public class Wave2d extends Applet implements ComponentListener {
static Wave2dFrame 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 Wave2dFrame(null);
ogf.init();
}
void showFrame() {
if (ogf == null) {
started = true;
ogf = new Wave2dFrame(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 Wave2dFrame extends Frame
implements ComponentListener, ActionListener, AdjustmentListener,
MouseMotionListener, MouseListener, ItemListener {
Thread engine = null;
Dimension winSize;
Image dbimage;
Random random;
int windowWidth = 50;
int windowHeight = 50;
int wallWidth;
public String getAppletInfo() {
return "Wave2d by Paul Falstad";
}
Checkbox stoppedCheck;
Checkbox intensityCheck;
Checkbox triChromaticCheck;
Checkbox symmCheck;
Checkbox infoCheck;
Choice setupChooser;
Vector setupList;
Setup setup;
Button resetTimeButton;
Scrollbar zoomBar;
Scrollbar angleBar;
Scrollbar freqBar;
Scrollbar resBar;
Scrollbar speedBar;
Scrollbar brightnessBar;
double colorMult;
double wavelength;
Label auxLabels[];
Scrollbar auxBars[];
static final int auxBarCount = 5;
static final double pi = 3.14159265358979323846;
static final int apertureHeight = 16;
float colorFunc[];
double apertureR[];
double apertureI[];
int dragX, dragY;
boolean dragging;
boolean dragClear;
boolean dragSet;
boolean recompute;
double t;
int pause;
MemoryImageSource imageSource;
int pixels[];
String muString = "u";
int getrand(int x) {
int q = random.nextInt();
if (q < 0) q = -q;
return q % x;
}
Wave2dCanvas cv;
Wave2d applet;
Wave2dFrame(Wave2d a) {
super("Wave2d Applet v1.2c");
applet = a;
}
boolean useBufferedImage = false;
public void init() {
setupList = new Vector();
Setup s = new SingleSlitSetup();
while (s != null) {
setupList.addElement(s);
s = s.createNext();
}
String os = System.getProperty("os.name");
int res = 120;
String jv = System.getProperty("java.class.version");
double jvf = new Double(jv).doubleValue();
muString = "u";
if (jvf >= 48) {
muString = "\u03bc";
useBufferedImage = true;
}
setLayout(new Wave2dLayout());
cv = new Wave2dCanvas(this);
cv.addComponentListener(this);
cv.addMouseMotionListener(this);
cv.addMouseListener(this);
add(cv);
setupChooser = new Choice();
int i;
for (i = 0; i != setupList.size(); i++)
setupChooser.add("Setup: " +
((Setup) setupList.elementAt(i)).getName());
setup = (Setup) setupList.elementAt(0);
setupChooser.addItemListener(this);
add(setupChooser);
intensityCheck = new Checkbox("Show Intensity", true);
intensityCheck.addItemListener(this);
add(intensityCheck);
stoppedCheck = new Checkbox("Stopped");
stoppedCheck.addItemListener(this);
stoppedCheck.disable();
add(stoppedCheck);
triChromaticCheck = new Checkbox("Tri-Chromatic", false);
triChromaticCheck.addItemListener(this);
add(triChromaticCheck);
/*symmCheck = new Checkbox("symm", false);
symmCheck.addItemListener(this);
add(symmCheck);*/
infoCheck = new Checkbox("Show Units", false);
infoCheck.addItemListener(this);
add(infoCheck);
add(resetTimeButton = new Button("Reset Time"));
resetTimeButton.addActionListener(this);
add(new Label("Incidence Angle", Label.CENTER));
add(angleBar = new Scrollbar(Scrollbar.HORIZONTAL, 90, 1, 10, 170));
angleBar.addAdjustmentListener(this);
add(new Label("Speed", Label.CENTER));
add(speedBar = new Scrollbar(Scrollbar.HORIZONTAL, 70, 1, 1, 200));
speedBar.addAdjustmentListener(this);
speedBar.disable();
add(new Label("Zoom Out", Label.CENTER));
add(zoomBar = new Scrollbar(Scrollbar.HORIZONTAL, 10, 1, 10, 200));
zoomBar.addAdjustmentListener(this);
add(new Label("Resolution", Label.CENTER));
add(resBar = new Scrollbar(Scrollbar.HORIZONTAL, res, 5, 120, 600));
resBar.addAdjustmentListener(this);
add(new Label("Source Frequency", Label.CENTER));
add(freqBar = new Scrollbar(Scrollbar.HORIZONTAL,
120, 1, 1, 236));
freqBar.addAdjustmentListener(this);
add(new Label("Brightness", Label.CENTER));
add(brightnessBar = new Scrollbar(Scrollbar.HORIZONTAL,
27, 1, 1, 1000));
brightnessBar.addAdjustmentListener(this);
auxLabels = new Label[auxBarCount];
auxBars = new Scrollbar[auxBarCount];
for (i = 0; i != auxBarCount; i++) {
add(auxLabels[i] = new Label("Aux " + (i+1), Label.CENTER));
add(auxBars[i] = new Scrollbar(Scrollbar.HORIZONTAL,
50, 1, 1, 100));
auxBars[i].addAdjustmentListener(this);
}
add(new Label(""));
try {
String param = applet.getParameter("PAUSE");
if (param != null)
pause = Integer.parseInt(param);
} catch (Exception e) { }
random = new Random();
setResolution();
reinit();
setup = (Setup) setupList.elementAt(0);
cv.setBackground(Color.black);
cv.setForeground(Color.lightGray);
resize(750, 600);
handleResize();
Dimension x = getSize();
Dimension screen = getToolkit().getScreenSize();
setLocation((screen.width - x.width)/2,
(screen.height - x.height)/2);
show();
}
void reinit() {
doSetup();
}
void apertureChanged() {
clearAperture();
setup.doAperture();
recompute = true;
}
void handleResize() {
Dimension d = winSize = cv.getSize();
if (winSize.width == 0)
return;
pixels = null;
if (useBufferedImage) {
try {
/* simulate the following code using reflection:
dbimage = new BufferedImage(d.width, d.height,
BufferedImage.TYPE_INT_RGB);
DataBuffer db = (DataBuffer)(((BufferedImage)dbimage).
getRaster().getDataBuffer());
DataBufferInt dbi = (DataBufferInt) db;
pixels = dbi.getData();
*/
Class biclass = Class.forName("java.awt.image.BufferedImage");
Class dbiclass = Class.forName("java.awt.image.DataBufferInt");
Class rasclass = Class.forName("java.awt.image.Raster");
Constructor cstr = biclass.getConstructor(
new Class[] { int.class, int.class, int.class });
dbimage = (Image) cstr.newInstance(new Object[] {
new Integer(d.width), new Integer(d.height),
new Integer(BufferedImage.TYPE_INT_RGB)});
Method m = biclass.getMethod("getRaster", null);
Object ras = m.invoke(dbimage, null);
Object db = rasclass.getMethod("getDataBuffer", null).
invoke(ras, null);
pixels = (int[])
dbiclass.getMethod("getData", null).invoke(db, null);
} catch (Exception ee) {
// ee.printStackTrace();
System.out.println("BufferedImage failed");
}
}
if (pixels == null) {
pixels = new int[d.width*d.height];
int i;
for (i = 0; i != d.width*d.height; i++)
pixels[i] = 0xFF000000;
imageSource = new MemoryImageSource(d.width, d.height, pixels, 0,
d.width);
imageSource.setAnimated(true);
imageSource.setFullBufferUpdates(true);
dbimage = cv.createImage(imageSource);
}
}
public boolean handleEvent(Event ev) {
if (ev.id == Event.WINDOW_DESTROY) {
applet.destroyFrame();
return true;
}
return super.handleEvent(ev);
}
void 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) {
}
boolean calculateNotice;
long lastTime;
public void updateWave2d(Graphics realg) {
if (winSize == null || winSize.width == 0) {
// this works around some weird bug in IE which causes the
// applet to not show up properly sometimes.
handleResize();
return;
}
if (!calculateNotice && recompute) {
FontMetrics fm = realg.getFontMetrics();
realg.setColor(Color.black);
String cs = "Calculating...";
realg.fillRect(0, winSize.height-30, 20+fm.stringWidth(cs), 30);
realg.setColor(Color.white);
realg.drawString(cs, 10, winSize.height-10);
cv.repaint(0);
calculateNotice = true;
return;
}
calculateNotice = false;
double tadd = 0;
if (!stoppedCheck.getState()) {
int val = speedBar.getValue();
tadd = Math.exp(val/20.)*(.1/5);
long sysTime = System.currentTimeMillis();
if (lastTime == 0)
lastTime = sysTime;
tadd *= (sysTime-lastTime)*(1/50.);
tadd *= freqBar.getValue() / 34.;
t += tadd;
lastTime = sysTime;
} else
lastTime = 0;
double trotr = Math.cos(t);
double troti = Math.sin(t);
int i, j;
boolean stopFunc = false;
if (stoppedCheck.getState())
stopFunc = true;
boolean obstacle = setup instanceof ObstacleSetup;
double zoom = (zoomBar.getValue()/10.);
if (recompute) {
recompute = false;
double fm = (angleBar.getValue()-90) * pi/180;
int apStart = -1, apEnd = -1;
// find width of area we need results for
int compWidth = (int) (zoom*windowWidth);
// find width of aperture
boolean symm0 = true;
boolean symm1 = true;
for (i = 0; i != wallWidth; i++) {
if (apertureR[i] != 0 || apertureI[i] != 0) {
if (apertureR[i] != apertureR[wallWidth-1-i] ||
apertureI[i] != apertureI[wallWidth-1-i])
symm1 = false;
if (i == 0 ||
apertureR[i] != apertureR[wallWidth-i] ||
apertureI[i] != apertureI[wallWidth-i])
symm0 = false;
apEnd = i;
if (apStart == -1)
apStart = i;
}
}
boolean symmetric = (symm0 || symm1) && !obstacle && fm == 0;
if (apStart == -1)
apStart = apEnd = wallWidth/2;
// compute how much wavefront data we need to compute for each line.
// -waveEnd, waveEnd = start and end of the wavefront data (where 0 is
// the center, directly under the aperture point). We need enough data
// for the right side of the aperture to reach the left side of the
// screen, and vice versa. We assume that the aperture is symmetric.
int waveEnd = wallWidth/2+compWidth/2-apStart;
// compute how big the FFT arrays have to be. They must be big enough
// to hold the result, plus enough offscreen space so that the waves do
// not wrap around to the other side of the screen. And they must be
// a power of 2.
int spaceNeeded = apEnd-apStart+1;
int symmStop = spaceNeeded/2;
if (symmetric)
compWidth = compWidth/2 + (apEnd-apStart)/2;
int sz = 1;
while (sz < compWidth + spaceNeeded)
sz *= 2;
/*System.out.println(symm0 + " " + symm1 + " " + symmetric);
System.out.println("ap " + apStart + " " + apEnd + " " +
"we " + waveEnd + " " +
"cw " + compWidth + " " +
sz + " " + zoom + " sn " + spaceNeeded);*/
int symmReflect = (symm0) ? sz : sz-1;
int szm = sz*2-1;
float line1[] = new float[sz*2];
float line2[] = new float[sz*2];
for (i = 0; i != wallWidth; i++)
if (apertureR[i] != 0 || apertureI[i] != 0) {
int ii = (i - wallWidth/2)*2;
int i0 = i - wallWidth/2;
double a = Math.cos(fm*i0);
double b = Math.sin(fm*i0);
line2[ii & szm] = (float) (a * apertureR[i] - b * apertureI[i]);
line2[(ii+1) & szm] =
(float) (a * apertureI[i] + b * apertureR[i]);
}
FFT fft = new FFT(sz);
fft.transform(line2, false);
//System.out.print("computing...");
int f = 0;
int fstart = triChromaticCheck.getState() ? 0 : 1;
int fend = triChromaticCheck.getState() ? 2 : 1;
if (intensityCheck.getState())
t = 1e8;
else
triChromaticCheck.setState(false);
int cfoff = 0;
for (f = fstart; f <= fend; f++) {
double m = freqBar.getValue()/50.;
if (f == 0)
m /= (650/510.); // red, 650 nm (green = 510 nm)
if (f == 2)
m /= (475/510.); // blue, 475 nm
double m0 = Math.sqrt(m);
wavelength = 2*pi/m;
double freq = 1/(2*pi);
double speed = wavelength * freq;
// if wavelength is smaller than 2 pixels, don't bother trying
// to draw waves.
boolean noWaves = wavelength < 2*zoom;
// if the reset time button gets hit, we have to recompute every
// frame until the wave this the edges of the screen.
// 1.23 =~ sqrt(1 + 1/2)
if (t < resBar.getValue()*zoom*1.23/speed)
recompute = true;
for (j = 0; j != windowHeight; j++) {
for (i = 0; i != sz*2; i++)
line1[i] = 0;
int jj = j+1;
double jz2 = jj*jj*zoom*zoom;
int i2;
int sz2 = sz*2;
// if reset time button was hit, limit wave train
double maxdist = t*speed-wavelength/8;
for (i = i2 = 0; i <= waveEnd; i++, i2 += 2) {
double dist1 = Math.sqrt(i*i+jz2);
double dist2 = dist1*m;
computeBessel(dist2);
if (dist1 > maxdist)
bessj0 = bessy0 = 0;
float f1 = (float) (bessj0*m0);
float f2 = (float) (-bessy0*m0);
if (i > 0) {
line1[sz2-i2] = f1;
line1[sz2-i2+1] = f2;
}
if (!symmetric || i < spaceNeeded) {
line1[i2] = f1;
line1[i2+1] = f2;
}
}
long t1 = System.currentTimeMillis();
fft.transform(line1, false);
for (i = 0; i != sz; i++) {
int ii = i*2;
float a = line1[ii]*line2[ii] -
line1[ii+1]*line2[ii+1];
float b = line1[ii+1]*line2[ii] +
line1[ii]*line2[ii+1];
line1[ii] = a;
line1[ii+1] = b;
}
fft.transform(line1, true);
float qmult = 400.f/sz;
if (obstacle) {
float oaddr = (float)(Math.cos(jj*zoom*m)*800/m0);
float oaddi = -(float)(Math.sin(jj*zoom*m)*800/m0);
int ww = (int) (windowWidth * zoom);
for (i = 0; i != ww; i++) {
int ii = i-ww/2;
float a = (float)Math.cos(fm*ii);
float b = (float)Math.sin(fm*ii);
float aa = (a * oaddr - b * oaddi) / qmult;
float bb = (a * oaddi + b * oaddr) / qmult;
line1[szm & (ii*2) ] = aa-line1[szm & (ii*2)];
line1[szm & (ii*2)+1] = bb-line1[szm & (ii*2)+1];
}
}
for (i = 0; i != windowWidth; i++) {
double ir1 = (int) ((i-windowWidth/2 )*zoom);
double ir2 = (int) ((i-windowWidth/2+1)*zoom);
int ii1 = (int) ir1;
int ii2 = (int) ir2;
int iic = ii2-ii1;
if (intensityCheck.getState())
colorFunc[cfoff] = 0;
else {
colorFunc[cfoff] = 0;
colorFunc[cfoff+1] = 0;
}
int ii;
for (ii = ii1; ii <= ii2; ii++) {
double q1 = 0;
double q2 = 0;
if (symmetric && ii >= symmStop) {
q1 = line1[szm & ((symmReflect-ii)*2)]*qmult;
q2 = line1[szm & ((symmReflect-ii)*2+1)]*qmult;
} else {
q1 = line1[szm & (ii*2 )]*qmult;
q2 = line1[szm & (ii*2+1)]*qmult;
}
double mu = .001;
if (ii == ii1)
mu *= 1-(ir1-ii1);
else if (ii == ii2)
mu *= ir2-ii2;
if (intensityCheck.getState())
colorFunc[cfoff] += (q1*q1+q2*q2) * mu;
else if (noWaves) {
// need to replace q1, q2 with the magnitude, or
// else the waves average out and cancel when
// using zoom
colorFunc[cfoff] += Math.sqrt(q1*q1+q2*q2) * mu;
} else {
colorFunc[cfoff ] += q1*mu;
colorFunc[cfoff+1] += q2*mu;
}
}
if (intensityCheck.getState()) {
colorFunc[cfoff++] /= ir2-ir1;
} else {
colorFunc[cfoff++] /= ir2-ir1;
colorFunc[cfoff++] /= ir2-ir1;
}
}
}
}
//System.out.println("done");
}
colorMult = 30*Math.exp(brightnessBar.getValue()/50.-10);
if (!intensityCheck.getState())
colorMult *= 1.5;
int ix = 0;
int k, l;
int height = winSize.height - apertureHeight;
int cfoff = 0;
int grncfadd = windowWidth*windowHeight;
int blucfadd = grncfadd*2;
double m = freqBar.getValue()/50.;
wavelength = 2*pi/m;
boolean noWaves = wavelength < 2*zoom;
for (j = 0; j != windowHeight; j++) {
for (i = 0; i != windowWidth; i++, cfoff++) {
int x = i*winSize.width/windowWidth;
int y = j*height/windowHeight + apertureHeight;
int x2 = (i+1)*winSize.width/windowWidth;
int y2 = (j+1)*height/windowHeight + apertureHeight;
int colval = 0;
if (intensityCheck.getState()) {
if (triChromaticCheck.getState())
colval = 0xFF000000 +
(getColorValue(cfoff) << 16) |
(getColorValue(cfoff+grncfadd) << 8) |
(getColorValue(cfoff+blucfadd));
else
colval = 0xFF000000 +
(getColorValue(cfoff) << 8);
} else {
double q1 = colorFunc[cfoff++];
double q2 = colorFunc[cfoff];
if (noWaves) {
int qq = (int) (q1*255);
if (qq > 255)
qq = 255;
colval = 0xFF000000 + qq*0x10100;
} else {
double q = (q1*trotr - q2*troti)*colorMult;
if (q > 0) {
int qq = (int) (q*255);
if (qq > 255)
qq = 255;
colval = 0xFF000000 + (qq << 8);
} else {
int qq = (int) (-q*255);
if (qq > 255)
qq = 255;
colval = 0xFF000000 + (qq << 16);
}
}
}
for (k = x; k < x2; k++)
for (l = y; l < y2; l++)
pixels[k+l*winSize.width] = colval;
}
}
//System.out.println(zoom + " " + windowWidth + " " + winSize.width);
for (i = 0; i != windowWidth; i++) {
double ir1 = (int) ((i-windowWidth/2 )*zoom);
double ir2 = (int) ((i-windowWidth/2+1)*zoom);
int ii1 = (int) ir1;
int ii2 = (int) ir2;
int iic = ii2-ii1;
int ii;
double funcr = 0, funcb = 0;
for (ii = ii1; ii <= ii2; ii++) {
//System.out.println(i + " " + ii + " " + zoom);
int ij = ii+wallWidth/2;
double mu = 1;
if (ii == ii1)
mu *= 1-(ir1-ii1);
else if (ii == ii2)
mu *= ir2-ii2;
if (ij < 0 || ij >= wallWidth) {
funcb += mu;
continue;
}
funcb += (1-(apertureR[ij]*apertureR[ij] +
apertureI[ij]*apertureI[ij]))*mu;
double ph = Math.atan2(apertureI[ij],
apertureR[ij])/pi;
if (ph < 0)
funcr += (2+ph)*mu;
else
funcr += ph*mu;
}
funcb /= ir2-ir1;
funcr /= (ir2-ir1)*2;
//System.out.println(i + " " + funcb);
if (obstacle)
funcb = 1-funcb;
int valb = (int) (funcb*255);
int valr = (int) (funcr*255);
if (valb < 0)
valb = 0;
if (valb > 255)
valb = 255;
if (valr < 0)
valr = 0;
if (valb > 255)
valb = 255;
int colval = 0xFF000000 + (valr << 16) | valb;
int x = i*winSize.width/windowWidth;
int x2 = (i+1)*winSize.width/windowWidth;
int y = 0;
int y2 = apertureHeight;
for (k = x; k < x2; k++)
for (l = y; l < y2; l++)
pixels[k+l*winSize.width] = colval;
}
if (imageSource != null)
imageSource.newPixels();
realg.drawImage(dbimage, 0, 0, this);
if (infoCheck.getState()) {
int aw = auxBars[0].getValue();
String s1 = setup.getInfo(0);
String s2 = setup.getInfo(1);
String s3 = "Screen height = " + getLength(zoom*windowHeight);
realg.setColor(Color.black);
FontMetrics fm = realg.getFontMetrics();
int ms = s1 == null ? 0 : fm.stringWidth(s1);
ms = s2 == null ? 0 : max(fm.stringWidth(s2), ms);
ms = max(fm.stringWidth(s3), ms);
int x = 20+ms;
int h = s1 == null ? (s2 == null ? 30 : 50) : 70;
realg.fillRect(winSize.width-x, winSize.height-h, x, h);
realg.setColor(Color.white);
if (s1 != null)
realg.drawString(s1, winSize.width-x+10, winSize.height-50);
if (s2 != null)
realg.drawString(s2, winSize.width-x+10, winSize.height-30);
realg.drawString(s3, winSize.width-x+10, winSize.height-10);
}
if (!intensityCheck.getState() && !stoppedCheck.getState())
cv.repaint(pause);
}
int max(int m1, int m2) { return (m1 > m2) ? m1 : m2; }
String getLength(double pix) {
NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(2);
double l = pix*510/wavelength;
if (l < 1e3)
return nf.format(l) + " nm";
if (l < 1e6)
return nf.format(l*1e-3) + " " + muString + "m";
if (l < 1e9)
return nf.format(l*1e-6) + " mm";
return nf.format(l*1e-9) + " m";
}
int getColorValue(int i) {
int val = (int) (colorFunc[i] * colorMult);
if (val > 255)
val = 255;
return val;
}
int abs(int x) {
return x < 0 ? -x : x;
}
int sign(int x) {
return (x < 0) ? -1 : (x == 0) ? 0 : 1;
}
public void componentHidden(ComponentEvent e){}
public void componentMoved(ComponentEvent e){}
public void componentShown(ComponentEvent e) {
cv.repaint();
}
public void componentResized(ComponentEvent e) {
handleResize();
cv.repaint(100);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == resetTimeButton) {
t = 0;
recompute = true;
cv.repaint();
}
}
public void adjustmentValueChanged(AdjustmentEvent e) {
System.out.print(((Scrollbar) e.getSource()).getValue() + "\n");
if (e.getSource() == resBar && resBar.getValue() != resBarValue)
setResolution();
int i;
for (i = 0; i != auxBarCount; i++)
if (e.getSource() == auxBars[i]) {
apertureChanged();
break;
}
if (e.getSource() == zoomBar || e.getSource() == angleBar ||
e.getSource() == freqBar)
recompute = true;
setResetTimeButton();
cv.repaint(pause);
}
void setResetTimeButton() {
// Reset Time button doesn't work well with obstacle or lo freqs
if (setup instanceof ObstacleSetup || freqBar.getValue() < 8 ||
intensityCheck.getState())
resetTimeButton.disable();
else
resetTimeButton.enable();
}
int resBarValue = -1;
void setResolution() {
resBarValue = windowWidth = windowHeight = resBar.getValue();
if ((windowWidth & 1) == 1)
windowWidth = windowHeight = resBarValue-1;
colorFunc = new float[windowWidth*windowHeight*3];
wallWidth = 512;
apertureR = new double[wallWidth];
apertureI = new double[wallWidth];
System.out.print(windowWidth + " " + windowHeight + "\n");
apertureChanged();
}
void setResolution(int x) {
resBar.setValue(x);
setResolution();
}
public void mouseDragged(MouseEvent e) {
dragging = true;
//edit(e);
cv.repaint(pause);
}
public void mouseMoved(MouseEvent e) {
if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0)
return;
int x = e.getX();
int y = e.getY();
dragX = x; dragY = y;
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == 0)
return;
dragging = true;
//edit(e);
}
public void mouseReleased(MouseEvent e) {
if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == 0)
return;
dragging = false;
dragSet = dragClear = false;
cv.repaint();
}
public void itemStateChanged(ItemEvent e) {
cv.repaint();
if (e.getItemSelectable() == symmCheck) {
recompute = true;
}
if (e.getItemSelectable() == triChromaticCheck ||
e.getItemSelectable() == intensityCheck) {
setResolution();
recompute = true;
if (intensityCheck.getState()) {
stoppedCheck.disable();
speedBar.disable();
triChromaticCheck.enable();
} else {
stoppedCheck.enable();
speedBar.enable();
triChromaticCheck.disable();
}
setResetTimeButton();
return;
}
if (e.getItemSelectable() == setupChooser)
doSetup();
}
void clearAperture() {
int i;
for (i = 0; i != wallWidth; i++)
apertureR[i] = apertureI[i] = 0;
}
void doSetup() {
t = 1e8;
int i;
for (i = 0; i != auxBarCount; i++)
auxBars[i].setValue(10);
freqBar.setValue(120);
zoomBar.setValue(10);
angleBar.setValue(90);
setup = (Setup)
setupList.elementAt(setupChooser.getSelectedIndex());
angleBar.enable();
setResetTimeButton();
setup.select();
apertureChanged();
for (i = 0; i < setup.getAuxBarCount(); i++) {
auxLabels[i].show();
auxBars[i].show();
}
for (; i < auxBarCount; i++) {
auxLabels[i].hide();
auxBars[i].hide();
}
validate();
}
abstract class Setup {
abstract String getName();
abstract void select();
abstract void doAperture();
abstract Setup createNext();
String getInfo(int x) { return null; }
int getAuxBarCount() { return 2; }
};
class SingleSlitSetup extends Setup {
String getName() { return "Single Slit"; }
void select() {
auxLabels[0].setText("Slit Width");
brightnessBar.setValue(440);
}
int w;
void doAperture() {
int x;
w = auxBars[0].getValue();
//System.out.println("width = " + w);
for (x = 0; x != w; x++)
apertureR[wallWidth/2 - w/2 + x] = 1;
}
int getAuxBarCount() { return 1; }
String getInfo(int x) {
if (x == 1)
return "Aperture width = " + getLength(w);
return null;
}
Setup createNext() { return new DoubleSlitSetup(); }
}
class DoubleSlitSetup extends Setup {
String getName() { return "Double Slit"; }
void select() {
auxLabels[0].setText("Slit Width");
auxLabels[1].setText("Separation");
auxLabels[2].setText("Balance");
auxLabels[3].setText("Phase Difference");
auxBars[2].setValue(50);
auxBars[3].setValue(1);
brightnessBar.setValue(380);
}
void doAperture() {
int x;
int w = auxBars[0].getValue();
int s = auxBars[1].getValue();
double bal2 = auxBars[2].getValue() / 100.;
double bal1 = 1-bal2;
if (bal1 > bal2) {
bal2 /= bal1;
bal1 = 1;
} else {
bal1 /= bal2;
bal2 = 1;
}
double ph = (auxBars[3].getValue()-1) * pi / 50.;
double a2r = bal2 * Math.cos(ph);
double a2i = bal2 * Math.sin(ph);
for (x = 0; x != w; x++) {
apertureR[wallWidth/2 - s - x] = bal1;
apertureR[wallWidth/2 + s + x] = a2r;
apertureI[wallWidth/2 + s + x] = a2i;
}
}
String getInfo(int x) {
if (x == 0)
return "Slit width = " + getLength(auxBars[0].getValue());
return "Separation = " + getLength(2*auxBars[1].getValue()-1);
}
int getAuxBarCount() { return 4; }
Setup createNext() { return new GratingSetup(); }
}
class GratingSetup extends Setup {
String getName() { return "Multiple Slits"; }
void select() {
auxLabels[0].setText("Slit Count");
auxLabels[1].setText("Slit Width");
auxLabels[2].setText("Separation");
brightnessBar.setValue(345);
}
int getAuxBarCount() { return 3; }
int w, s;
void doAperture() {
int x;
w = auxBars[1].getValue();
s = auxBars[2].getValue()+3;
if (w > s-1)
w = s-1;
int i;
int n = auxBars[0].getValue()/5+1;
int sub = 0;
while (true) {
sub = (s*(n-1)+w)/2;
if (-sub+s*(n-1)+w < wallWidth/2)
break;
n--;
}
for (x = 0; x != w; x++)
for (i = 0; i != n; i++)
apertureR[wallWidth/2-sub+s*i+x] = 1;
}
String getInfo(int x) {
if (x == 0)
return "Slit width = " + getLength(w);
return "Separation = " + getLength(s-w);
}
Setup createNext() { return new ObstacleSetup(); }
}
class ObstacleSetup extends Setup {
String getName() { return "Obstacle"; }
void select() {
auxLabels[0].setText("Width");
// waves with an incidence angle don't work for some reason
// with obstacles, so disable them
angleBar.disable();
angleBar.setValue(90);
// don't feel like getting Reset Time to work with Obstacle
resetTimeButton.disable();
brightnessBar.setValue(310);
}
int getAuxBarCount() { return 1; }
int w;
void doAperture() {
int x;
w = (int) (auxBars[0].getValue()*1.5);
for (x = 0; x != w; x++)
apertureR[wallWidth/2-w/2+x] = 1;
}
String getInfo(int x) {
if (x == 1)
return "Width = " + getLength(w);
return null;
}
Setup createNext() { return new ZonePlateEvenSetup(); }
}
class ZonePlateEvenSetup extends Setup {
String getName() { return "Zone Plate (Even)"; }
int evenOdd = 0;
boolean phase = false;
boolean blazed = false;
void select() {
auxLabels[0].setText("Intended Frequency");
auxBars[0].setValue(freqBar.getValue() * 100 / 236);
auxLabels[1].setText("Focal Length");
auxBars[1].setValue(20);
auxLabels[2].setText("Plate Width");
auxBars[2].setValue(100);
brightnessBar.setValue(111);
}
int getAuxBarCount() { return 3; }
void doAperture() {
int i;
// freqBar ranges from 0 to 4.72, so we match that here
double m = auxBars[0].getValue() * .0472;
double halfwave = pi/m;
int pw = auxBars[2].getValue() * 3;
int dy = auxBars[1].getValue()*5;
int cx = wallWidth/2;
for (i = 1; i != wallWidth; i++) {
int dx = cx-i;
if (dx < -pw || dx > pw)
continue;
double dist = Math.sqrt(dx*dx+dy*dy);
dist = (dist-dy);
if (blazed) {
double ph = dist/halfwave * pi;
apertureR[i] = Math.cos(ph);
apertureI[i] = Math.sin(ph);
} else {
int zone = (int) (dist / halfwave);
apertureR[i] = ((zone & 1) == evenOdd) ? 1 :
(phase) ? -1 : 0;
}
}
}
Setup createNext() { return new ZonePlateOddSetup(); }
}
class ZonePlateOddSetup extends ZonePlateEvenSetup {
ZonePlateOddSetup() { evenOdd = 1; }
String getName() { return "Zone Plate (Odd)"; }
Setup createNext() { return new ZonePlatePhaseSetup(); }
}
class ZonePlatePhaseSetup extends ZonePlateOddSetup {
ZonePlatePhaseSetup() { phase = true; }
String getName() { return "Phase-Reversal Zone Plate"; }
Setup createNext() { return new ZonePlateBlazedSetup(); }
}
class ZonePlateBlazedSetup extends ZonePlateOddSetup {
ZonePlateBlazedSetup() { blazed = true; }
String getName() { return "Blazed Zone Plate"; }
Setup createNext() { return new Hologram1Setup(); }
}
class Hologram1Setup extends Setup {
String getName() { return "Absorption Hologram"; }
void select() {
auxLabels[0].setText("Intended Frequency");
auxBars[0].setValue(freqBar.getValue() * 100 / 236);
auxLabels[1].setText("X 1");
auxLabels[2].setText("Y 1");
auxLabels[3].setText("X 2");
auxLabels[4].setText("Y 2");
auxBars[1].setValue(40);
auxBars[2].setValue(15);
auxBars[3].setValue(70);
auxBars[4].setValue(40);
brightnessBar.setValue(285);
zoomBar.setValue(15);
}
int getAuxBarCount() { return 5; }
void doAperture() {
int i;
// freqBar ranges from 0 to 4.72, so we match that here
double m = auxBars[0].getValue() * .0472;
double halfwave = pi/m;
int px1 = auxBars[1].getValue() * 2 - 100;
int py1 = auxBars[2].getValue() * 3+5;
int px2 = auxBars[3].getValue() * 2 - 100;
int py2 = auxBars[4].getValue() * 3+5;
int cx = wallWidth/2;
int pw = 300;
double maxf = 0;
double aradd = .75;
for (i = 0; i != wallWidth; i++) {
if (i-cx < -pw || i-cx > pw)
continue;
int dx = px1-(i-cx);
double dist = Math.sqrt(dx*dx+py1*py1);
computeBessel(dist*m);
double ar = bessj0, ai = bessy0;
dx = px2-(i-cx);
dist = Math.sqrt(dx*dx+py2*py2);
computeBessel(dist*m);
ar += bessj0;
ai += bessy0;
ar += aradd;
double q = apertureR[i] = Math.sqrt(ar*ar+ai*ai);
if (q > maxf)
maxf = q;
}
maxf = Math.sqrt(maxf);
for (i = 0; i != wallWidth; i++)
apertureR[i] /= maxf;
}
Setup createNext() { return new Hologram2Setup(); }
}
class Hologram2Setup extends Setup {
String getName() { return "Phase Hologram"; }
void select() {
auxLabels[0].setText("Intended Frequency");
auxBars[0].setValue(freqBar.getValue() * 100 / 236);
auxLabels[1].setText("X 1");
auxLabels[2].setText("Y 1");
auxLabels[3].setText("X 2");
auxLabels[4].setText("Y 2");
auxBars[1].setValue(40);
auxBars[2].setValue(15);
auxBars[3].setValue(70);
auxBars[4].setValue(40);
brightnessBar.setValue(150);
zoomBar.setValue(15);
}
int getAuxBarCount() { return 5; }
void doAperture() {
int i;
// freqBar ranges from 0 to 4.72, so we match that here
double m = auxBars[0].getValue() * .0472;
double halfwave = pi/m;
int px1 = auxBars[1].getValue() * 2 - 100;
int py1 = auxBars[2].getValue() * 3+5;
int px2 = auxBars[3].getValue() * 2 - 100;
int py2 = auxBars[4].getValue() * 3+5;
int cx = wallWidth/2;
int pw = 300;
double maxf = 0;
for (i = 0; i != wallWidth; i++) {
if (i-cx < -pw || i-cx > pw)
continue;
int dx = px1-(i-cx);
double dist = Math.sqrt(dx*dx+py1*py1);
computeBessel(dist*m);
apertureR[i] = bessj0;
apertureI[i] = bessy0;
dx = px2-(i-cx);
dist = Math.sqrt(dx*dx+py2*py2);
computeBessel(dist*m);
apertureR[i] += bessj0;
apertureI[i] += bessy0;
double q =
apertureR[i]*apertureR[i] +
apertureI[i]*apertureI[i];
if (q > maxf)
maxf = q;
}
maxf = Math.sqrt(maxf);
for (i = 0; i != wallWidth; i++) {
apertureR[i] /= maxf;
apertureI[i] /= maxf;
}
}
Setup createNext() { return null; }
}
double bessj0, bessy0;
void computeBessel(double x) {
double ax = x,z;
double xx,y,ans,ans1,ans2;
if (x < 8.0) {
y=x*x;
ans1=57568490574.0+y*(-13362590354.0+y*(651619640.7
+y*(-11214424.18+y*(77392.33017+y*(-184.9052456)))));
ans2=57568490411.0+y*(1029532985.0+y*(9494680.718
+y*(59272.64853+y*(267.8532712+y*1.0))));
bessj0 = ans=ans1/ans2;
ans1 = -2957821389.0+y*(7062834065.0+y*(-512359803.6
+y*(10879881.29+y*(-86327.92757+y*228.4622733))));
ans2=40076544269.0+y*(745249964.8+y*(7189466.438
+y*(47447.26470+y*(226.1030244+y*1.0))));
bessy0=(ans1/ans2)+0.636619772*bessj0*Math.log(x);
} else {
z=8.0/ax;
y=z*z;
xx=ax-0.785398164;
if (x > 83) {
ans1 = 1;
ans2 = -.0156249;
} else {
ans1=1.0+y*(-0.1098628627e-2+y*(0.2734510407e-4
+y*(-0.2073370639e-5+y*0.2093887211e-6)));
ans2 = -0.1562499995e-1+y*(0.1430488765e-3
+y*(-0.6911147651e-5+y*(0.7621095161e-6
-y*0.934935152e-7)));
}
double sax = Math.sqrt(0.636619772/ax);
double cosxx = Math.cos(xx);
double sinxx = Math.sin(xx);
ans2 *= z;
bessj0 = sax * (cosxx*ans1-sinxx*ans2);
bessy0 = sax * (sinxx*ans1+cosxx*ans2);
}
}
}
class FFT {
float wtabf[];
float wtabi[];
int size;
FFT(int sz) {
size = sz;
if ((size & (size-1)) != 0)
System.out.println("size must be power of two!");
calcWTable();
}
void calcWTable() {
// calculate table of powers of w
wtabf = new float[size];
wtabi = new float[size];
int i;
for (i = 0; i != size; i += 2) {
double pi = 3.1415926535;
double th = pi*i/size;
wtabf[i ] = (float)Math.cos(th);
wtabf[i+1] = (float)Math.sin(th);
wtabi[i ] = wtabf[i];
wtabi[i+1] = -wtabf[i+1];
}
}
void transform(float data[], boolean inv) {
int i;
int j = 0;
int size2 = size*2;
if ((size & (size-1)) != 0)
System.out.println("size must be power of two!");
// bit-reversal
float q;
int bit;
for (i = 0; i != size2; i += 2) {
if (i > j) {
q = data[i]; data[i] = data[j]; data[j] = q;
q = data[i+1]; data[i+1] = data[j+1]; data[j+1] = q;
}
// increment j by one, from the left side (bit-reversed)
bit = size;
while ((bit & j) != 0) {
j &= ~bit;
bit >>= 1;
}
j |= bit;
}
// amount to skip through w table
int tabskip = size << 1;
float wtab[] = (inv) ? wtabi : wtabf;
int skip1, skip2, ix, j2;
float wr, wi, d1r, d1i, d2r, d2i, d2wr, d2wi;
// unroll the first iteration of the main loop
for (i = 0; i != size2; i += 4) {
d1r = data[i];
d1i = data[i+1];
d2r = data[i+2];
d2i = data[i+3];
data[i ] = d1r+d2r;
data[i+1] = d1i+d2i;
data[i+2] = d1r-d2r;
data[i+3] = d1i-d2i;
}
tabskip >>= 1;
// unroll the second iteration of the main loop
int imult = (inv) ? -1 : 1;
for (i = 0; i != size2; i += 8) {
d1r = data[i];
d1i = data[i+1];
d2r = data[i+4];
d2i = data[i+5];
data[i ] = d1r+d2r;
data[i+1] = d1i+d2i;
data[i+4] = d1r-d2r;
data[i+5] = d1i-d2i;
d1r = data[i+2];
d1i = data[i+3];
d2r = data[i+6]*imult;
d2i = data[i+7]*imult;
data[i+2] = d1r-d2i;
data[i+3] = d1i+d2r;
data[i+6] = d1r+d2i;
data[i+7] = d1i-d2r;
}
tabskip >>= 1;
for (skip1 = 16; skip1 <= size2; skip1 <<= 1) {
// skip2 = length of subarrays we are combining
// skip1 = length of subarray after combination
skip2 = skip1 >> 1;
tabskip >>= 1;
for (i = 0; i != 1000; i++);
// for each subarray
for (i = 0; i < size2; i += skip1) {
ix = 0;
// for each pair of complex numbers (one in each subarray)
for (j = i; j != i+skip2; j += 2, ix += tabskip) {
wr = wtab[ix];
wi = wtab[ix+1];
d1r = data[j];
d1i = data[j+1];
j2 = j+skip2;
d2r = data[j2];
d2i = data[j2+1];
d2wr = d2r*wr - d2i*wi;
d2wi = d2r*wi + d2i*wr;
data[j] = d1r+d2wr;
data[j+1] = d1i+d2wi;
data[j2 ] = d1r-d2wr;
data[j2+1] = d1i-d2wi;
}
}
}
}
}
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
u-xi.com.cn
www.youcaiqi.com.cn
zkrcl.com.cn
www.muchu6.net.cn
www.yegai0.com.cn
www.dawan7.com.cn
www.doumi7.com.cn
www.008fei.com.cn
www.lizhe0.net.cn
www.215269.org.cn