/*******************************************************************************
class Moire1 implements an animated Moire pattern. A Moire patterns occurs when
two similar patters are almost superimposed. There is a kind of visual appearance.
In this case, the pattern consists of lines radiating out from a common center.
one such pattern is drawn with the lines radiating out from the center of the
applet. A second pattern has a center that drifts about, producing a changing
interference pattern.
You can also click and drag on the applet to move the second pattern about by
hand.
Several parameters can be set by tags:
Name Default Legal values Meaning
--------- --------- --------------- -----------------------------------
lineCount 36 1 to 100 Number of lines drawn in each set
of lines. (Note: each "line"
gives two "spokes" radiating from
the center.)
lineColor red any color Color of the lines.
bgColor cyan any color Color seen behind the lines.
sleepTime 25 1 to 5000 Time, in milliseconds, between
movements of the pattern; smaller
values give faster movements.
border 0 0 to 50 Width of border drawn around the
Moire pattern; note that the
default is to have no border.
borderColor blue any color Color of the border.
Note that a color can be specified either as a set of three integers between
0 and 255, giving the red, blue, and green components of the color, or it
can be specified as one of the built-in color names: white, black, red, green,
blue, yellow, cyan, magneta, pink, orange, gray, lightGray, or darkGray.
Color names are not case sensitive, although param names are.
Note: You might find the method getColorParam() useful in other applications.
BY: David Eck
Department of Mathematics and Computer Science
Hobart and William Smith Colleges
Geneva, NY 14456
E-mail: eck@hws.edu
NOTE: YOU CAN DO ANYTHING YOU WANT WITH THIS CODE AND APPLET, EXCEPT
TRY TO COPYRIGHT OR PATENT THEM YOURSELF.
*******************************************************************************/
import java.awt.*;
import java.util.Random;
public class Moire1 extends java.applet.Applet implements Runnable {
static final Random rand = new Random();
Thread runner; // thread to produce the animation
Image buffer = null; // an off-screen canvas
int w,h; // width and height of the buffer
double center_x, center_y; // current position of the center of
// the second set of lines.
int cx,cy; // for use in mouseDrag, mouseExit;
// position of center during dragging
int mouseStart_x, mouseStart_y; // used during dragging to hold the
// location of the original mouse click
boolean dragging = false; // set to "true" while dragging is in
// progress, as signal to "run" method
// to pause the regular animation.
boolean stopped = false; // toggled when user shift-clicks on the
// applet.
int sleepTime = 25; // applet 's, described above
Color backColor = Color.cyan;
Color lineColor = Color.red;
int lines = 36;
int border = 0;
Color borderColor = Color.blue;
double[] cos, sin; // hold sines and cosines of angle; one for each line
// line to be drawn. This just avoids recomputing this
// all the time.
public void init() {
Color c;
Integer temp;
if ( (c = getColorParam("bgColor")) != null )
backColor = c;
if ( (c = getColorParam("lineColor")) != null )
lineColor = c;
if ( (temp = getIntParam("lineCount")) != null )
lines = temp.intValue();
if (lines < 1 || lines > 100)
lines = 36;
if ( (temp = getIntParam("sleepTime")) != null )
sleepTime = temp.intValue();
if (sleepTime < 1 || sleepTime > 5000)
sleepTime = 25;
if ( (temp = getIntParam("border")) != null )
border = temp.intValue();
if (border < 0 || border > 50)
border = 0;
if ( (c = getColorParam("borderColor")) != null )
borderColor = c;
setBackground(borderColor);
double delta = 180.0 / lines;
cos = new double[lines];
sin = new double[lines];
for (int i = 0; i < lines; i++) {
double angle = ((i * delta * Math.PI)/180.0);
cos[i] = Math.cos(angle);
sin[i] = Math.sin(angle);
}
}
Integer getIntParam(String paramName) {
// retrieve an integer ; return null if the specified
// param is not present or if the value is illegal
String param = getParameter(paramName);
if (param == null)
return null;
int i;
try {
i = Integer.parseInt(param);
}
catch (NumberFormatException e) {
return null;
}
return new Integer(i);
}
Color getColorParam(String paramName) {
// retrieve a color ; return null if the specified
// param is not present or if the value is illegal. Legal
// values include the 13 named Java colors ("red", "black",
// etc.) and RGB values given as 3 integers between 0 and 255.
// Color names are not case sensitive. Integers in RGB
// colors can be separated by any non-digit characters.
String param = getParameter(paramName);
if (param == null || param.length() == 0)
return null;
if (Character.isDigit(param.charAt(0))) { // try to parse RGB color
int r=0,g=0,b=0;
int pos=0;
int d=0;
int len=param.length();
while (pos < len && Character.isDigit(param.charAt(pos)) && r < 255) {
d = Character.digit(param.charAt(pos),10);
r = 10*r + d;
pos++;
}
if (r > 255)
return null;
while (pos < len && !Character.isDigit(param.charAt(pos)))
pos++;
if (pos >= len)
return null;
while (pos < len && Character.isDigit(param.charAt(pos)) && g < 255) {
d = Character.digit(param.charAt(pos),10);
g = 10*g + d;
pos++;
}
if (g > 255)
return null;
while (pos < len && !Character.isDigit(param.charAt(pos)))
pos++;
if (pos >= len)
return null;
while (pos < len && Character.isDigit(param.charAt(pos)) && b < 255) {
d = Character.digit(param.charAt(pos),10);
b = 10*b + d;
pos++;
}
if (b > 255)
return null;
return new Color(r,g,b);
}
param.toLowerCase();
if (param.equals("black"))
return Color.black;
if (param.equals("white"))
return Color.white;
if (param.equals("red"))
return Color.red;
if (param.equals("green"))
return Color.green;
if (param.equals("blue"))
return Color.blue;
if (param.equals("yellow"))
return Color.yellow;
if (param.equals("cyan"))
return Color.cyan;
if (param.equals("magenta"))
return Color.magenta;
if (param.equals("pink"))
return Color.pink;
if (param.equals("orange"))
return Color.orange;
if (param.equals("gray"))
return Color.gray;
if (param.equals("darkgray"))
return Color.darkGray;
if (param.equals("lightgray"))
return Color.lightGray;
return null; // param is not a legal color
}
public void start() {
if (runner == null && !stopped) {
runner = new Thread(this);
runner.start();
}
}
public void stop() {
if (runner != null) {
runner.stop();
runner = null;
}
}
public boolean mouseDown(Event evt, int x, int y) {
// begin dragging second set of lines.
if (evt.shiftDown()) {
stopped = !stopped;
if (stopped)
stop();
else
start();
}
else if (buffer != null) {
mouseStart_x = x;
mouseStart_y = y;
dragging = true;
}
return true;
}
public boolean mouseDrag(Event evt, int x, int y) {
if (dragging) {
int offset_x = x - mouseStart_x;
int offset_y = y - mouseStart_y;
cx = (int)center_x + offset_x;
cy = (int)center_y + offset_y;
Graphics g = buffer.getGraphics();
makeMoire(g,w,h,cx,cy);
repaint();
g.dispose();
}
return true;
}
public boolean mouseExit(Event evt, int x, int y) {
// Netscape won't send a mouseUp if the button is released outside the applet!!
// AppletViewer, on the other hand, won't send a mouseExit during dragging,
// which seems to me to be the correct behavior.
if (dragging) {
dragging = false;
center_x = cx;
center_y = cy;
}
return true;
}
public boolean mouseUp(Event evt, int x, int y) {
if (dragging) {
dragging = false;
center_x = center_x + x - mouseStart_x;
center_y = center_y + y - mouseStart_y;
}
return true;
}
synchronized void putMoire(Graphics g) {
// copy buffer to g, leaving space for the border
g.drawImage(buffer,border,border,this);
}
public void paint(Graphics g) { // paint method just copies canvas to applet
if (buffer != null)
putMoire(g);
}
public void update(Graphics g) { // replace standard update method so it doesn't
// erase the screen
paint(g);
}
synchronized void makeMoire(Graphics g, int w, int h, int cx, int cy) {
// Create the Moire pattern in the buffer. w and h give the
// size of the buffer (and yes, that could have been gotten from g).
// cx and cy give the position of the center of the second set of lines.
g.setColor(backColor);
g.fillRect(0,0,w,h);
g.setColor(lineColor);
drawMoire(g,2*w,2*h,w/2,h/2);
drawMoire(g,2*w,2*h,cx,cy);
}
void drawMoire(Graphics g, int width, int height, int center_x, int center_y) {
// one set of lines, with center at center_x, center_y
int s = ( (width > height)? width : height );
s = (int)((double)s * 0.72);
for (int i = 0; i < lines; i++) {
int x = (int)(s*cos[i]);
int y = (int)(s*sin[i]);
g.drawLine(center_x + x, center_y + y, center_x - x, center_y - y);
}
}
public void run() { // run method for animation thread
if (buffer == null) {
w = size().width - 2*border; // get the width and height of the canvas
h = size().height - 2*border; // (Applet is not effectively resizable.)
buffer = createImage(w,h); // create an off screen canvas of the same size
}
center_x = w / 2; // initial position of center of second set of lines
center_y = h / 2;
double x_min = center_x - 25; // limits for wandering of second set of lines
double x_max = center_x + 25;
double y_min = center_y - 25;
double y_max = center_y + 25;
double dx, dy; // amount by which center should move at each step
do {
dx = 5 * (rand.nextDouble() - 0.5);
dy = 5 * (rand.nextDouble() - 0.5);
} while (dx*dx + dy*dy < 1);
while (true) {
if (!dragging) {
center_x += dx; // drift by amount (dx,dy)
center_y += dy;
if (center_x < x_min) { // make sure motion is towards the right
do {
dx = rand.nextDouble() * 2.5;
} while (dx*dx + dy*dy < 1);
}
else if (center_x > x_max) { // make sure motion is towards the left
do {
dx = - rand.nextDouble() * 2.5;
} while (dx*dx + dy*dy < 1);
}
if (center_y < y_min) { // make sure motion is downwards
do {
dy = rand.nextDouble() * 2.5;
} while (dx*dx + dy*dy < 1);
}
else if (center_y > y_max) { // make sure motion is upwards
do {
dy = - rand.nextDouble() * 2.5;
} while (dx*dx + dy*dy < 1);
}
if (rand.nextDouble() < 0.01) { // Occasionally, change motion vector at random
do {
dx = 5 * (rand.nextDouble() - 0.5);
dy = 5 * (rand.nextDouble() - 0.5);
} while (dx*dx + dy*dy < 1);
}
Graphics g = buffer.getGraphics(); // create and display the pattern.
makeMoire(g,w,h,(int)center_x,(int)center_y);
g.dispose();
repaint();
}
try
{ Thread.sleep(sleepTime); }
catch (InterruptedException e)
{ }
}
}
}