import java.awt.*;
import java.applet.*;
import java.awt.image.*;
import java.awt.event.*;
public class SinusMap extends Applet implements Runnable, KeyListener
{
Thread runner = null;
//Frames per sec messeasueres.
long startTime = 0;
double ftp = 0;
int nrOfFrames = 0;
//Next som boring dimensions variabels, mainly used for copying parts of the image
//to other parts.
int width = 400;
int height = 200;
int nrOfPixels = width*height;
int halfWidth = width/2;
int halfWidthMinusOne = halfWidth-1;
int halfHeight = height/2;
int halfHeightMinusOne = halfHeight-1;
double widthIncrease = 1F/(width);
double heightIncrease = 1F/(height);
double m = 0; //the sinus stretch factor.
float n = 0; //the zoom factor.
int k = 0; //k is used together with n, to time the zoom in and out routine.
SineTexture st = new SineTexture(m, m, 2);
//Controles.
boolean zoom = false;
boolean colorcycle = true;
int speed = 10;
boolean blend = false;
boolean mix = false;
boolean rotate = false;
boolean stop = false;
int angel = 1;
Font font = new Font("arial", Font.PLAIN, 30);
WritableImage wi; //The image witch we manipulate all the time.
public void init()
{
startTime= System.currentTimeMillis();
new SinCosTabel();
addKeyListener(this);
setSize(width, height);
wi = new WritableImage(width,height);
calculateN();
}
public void start()
{
runner = new Thread(this);
runner.start();
}
public void stop()
{
runner.interrupt();
runner = null;
}
void calculateN()
{
widthIncrease = 1F/(width/(n+1));
heightIncrease = 1F/(height/(n+1));
}
public void update(Graphics g)
{
if(zoom)
{
if((k/50)%2 == 0)
n += 0.1F;
else
n -= 0.1F;
k++;
//if we have changed the dimensions of the texture we have to recalculate the space between
//pixels at the x and y axel.
calculateN();
}
if(!stop)
st.change(m, m);
if(colorcycle)
st.changeColor();
double heightMove = 0;
double widthMove = 0;
int color, newY, newX, oldY=0, oldX=0;
//calculate pixels for upper right quadrant of the sinus texture.
for(int y = 0; y < halfHeight; y++)
{ widthMove = 0;
for(int x = 0; x < halfWidth; x++)
{
if(rotate)
{ oldX = x; oldY = y;
newX = (int)(x * SinCosTabel.cos[angel] - y * SinCosTabel.sin[angel]);
newY = (int)(x * SinCosTabel.sin[angel] + y * SinCosTabel.cos[angel]);
x = newX; y = newY;
}
if(x >= 0 && x < halfWidth && y >= 0 && y > 8))+1);
int newRed = 255 - Math.abs((0xffffff00 | (color >> 16))+1);
int oldBlue = 255-Math.abs((0xffffff00 | oldColor)+1);
int oldGreen = 255-Math.abs((0xffffff00 | (oldColor >> 8))+1);
int oldRed = 255 - Math.abs((0xffffff00 | (oldColor >> 16))+1);
color = 0xff000000 | ((newRed+oldRed)>>1/*div with 2*/) << 16
| ((newGreen+oldGreen)>>1) << 8
| ((newBlue+oldBlue)>>1) << 0;
}
if(blend)
color = (int)((color+wi.pixels[dY+deformedX])/2);
//We only calculate the pixels to the upper right quadrant of the picture.
//Then we simply copy the quadrant to the others, by miorring it. =>
wi.pixels[dY+deformedX] = color;
wi.pixels[dY+dX] = color;
wi.pixels[dY2+deformedX] = color;
wi.pixels[dY2+dX] = color;
widthMove += widthIncrease;
}
if(rotate)
{ x = oldX; y = oldY; }
}
heightMove += heightIncrease;
}
((Graphics2D)g).drawImage(wi.getNextFrame(), 0, 0, this);
if(!stop)
m += 0.2;
if(rotate)
{ angel++;
if(angel == 90)
angel = 0;
}
//Doing frames pr sec calculations.
nrOfFrames++;
if(nrOfFrames == 10)
{
startTime = System.currentTimeMillis() - startTime;
if(startTime != 0)
ftp = (double)(5000/startTime);
else
ftp = 1000;
startTime = System.currentTimeMillis();
nrOfFrames = 0;
}
((Graphics2D)g).setColor(Color.white);
((Graphics2D)g).drawString("FPS: "+ftp, 0, 20);
}
public void run()
{
try
{ while(!runner.interrupted())
{
repaint();
runner.sleep(speed);
}
}catch(InterruptedException e) {}
}
public void keyPressed(java.awt.event.KeyEvent evt)
{
char code = evt.getKeyChar();
int intCode = evt.getKeyCode();
switch(intCode)
{ case KeyEvent.VK_UP: n += 0.1; calculateN(); break;
case KeyEvent.VK_DOWN: n -= 0.1; calculateN(); break;
case KeyEvent.VK_LEFT: if(m > 0.4) m -= 0.2; break;
case KeyEvent.VK_RIGHT: m += 0.2; break;
}
switch(code)
{
case '1': st.type = st.RING; break;
case '2': st.type = st.SQUARE; break;
case '3': st.type = st.WAVE; break;
case '4': st.type = st.SPIKEWAVE; break;
case '5': st.type = st.ELIPSE; break;
case '6': st.type = st.PARABEL; break;
case '7': st.type = st.LINES; break;
case '8': st.type = st.CELLS; break;
case '9': st.type = st.CULLS; break;
case '0': st.type = st.EXP; break;
case 'z': zoom = !zoom; break;
case 'c': colorcycle = !colorcycle; break;
case 'q': st.modFunction = 2; break;
case 'w': st.modFunction = 3; break;
case 'e': st.modFunction = 4; break;
case 't': st.modFunction = 5; break;
case '-': speed += 2; break;
case '+': if(speed > 2) speed -= 2; break;
case 'b': blend = !blend; break;
case 'm': mix = !mix; break;
case 'r': rotate = !rotate; break;
case 's': stop = !stop; break;
default: break;
}
}
public void keyTyped(java.awt.event.KeyEvent evt) {}
public void keyReleased(java.awt.event.KeyEvent evt) {}
}
class SineTexture
{
double multiplier, scale;
int modFunction;
int n = 0;
int m = 0;
static final int RING = 0;
static final int SQUARE = 1;
static final int WAVE = 2;
static final int SPIKEWAVE = 3;
static final int ELIPSE = 4;
static final int PARABEL = 5;
static final int EXP = 6;
static final int LINES = 7;
static final int CELLS = 8;
static final int CULLS = 9;
static int type = SQUARE;
SineTexture (double multiplier, double scale, int modFunction)
{
this.multiplier = multiplier;
this.scale = scale;
this.modFunction = modFunction;
}
public void change(double multiplier, double scale)
{ this.multiplier = Math.exp(multiplier);
this.scale = scale;
}
public void changeColor()
{
if((m/255)%2 == 0)
n++;
else n--;
m++;
}
public int getTexel (double i, double j)
{
double f = 0;
switch(type)
{ case SQUARE: f = scale * (Math.tan(j) + Math.sin(i)); break;
case RING: f = scale * (Math.cos(j) + Math.cos(i)); break;
case WAVE: f = scale * (Math.cos(j) + Math.sin(i)); break;
case SPIKEWAVE: f = scale * (Math.acos(j) + Math.sin(i)); break;
case ELIPSE: f = scale * (Math.acos(j) + Math.cos(i)); break;
case PARABEL: f = scale * i*i + scale*j + i; break;
case LINES: f = scale * i + j; break;
case CELLS: f = scale *i*j + j; break;
case CULLS: f = Math.sqrt(scale * (Math.cos(j) + Math.sin(i))scale *scale *j * j*i); break;
case EXP: f = Math.sqrt(scale *scale *j * i ); break;
}
int revN = Math.abs(n-255);
switch((int)(f % modFunction))
{ case 0: return 0xff000000 | revN << 16 | 0 << 8 | 0<< 0;
case 1: return 0xff000000 | 0 << 16 | n << 8 | revN << 0;
case 2: return 0xff000000 | n << 16 | revN << 8 | 0 << 0;
case 3: return 0xff000000 | 0 << 16 | revN << 8 | n << 0;
default: return 0xff000000 | n << 16 | revN << 8 | revN << 0;
}
}
}
class SinCosTabel
{
public static double sin[] = new double[361];
public static double cos[] = new double[361];
SinCosTabel()
{
for(int i = 0; i < 361; i++)
{
sin[i] = Math.sin(Math.toRadians(i));
cos[i] = Math.cos(Math.toRadians(i));
}
}
}
class WritableImage implements ImageProducer
{
ImageConsumer consumer;
Image image = null;
int width,height;
ColorModel cm;
int[] pixels;
private int hints,sfd;
WritableImage(int width, int height)
{
this.width=width;
this.height=height;
this.cm = new DirectColorModel(32,0xFF0000,0xFF00,0xFF);
this.pixels = new int[width*height];
hints = ImageConsumer.TOPDOWNLEFTRIGHT
| ImageConsumer.COMPLETESCANLINES
| ImageConsumer.SINGLEPASS
| ImageConsumer.SINGLEFRAME;
sfd = ImageConsumer.SINGLEFRAMEDONE;
image = Toolkit.getDefaultToolkit().createImage(this);
}
public Image getNextFrame()
{
update();
return image;
}
public synchronized void addConsumer(ImageConsumer consumer)
{
this.consumer = consumer;
}
public final void startProduction(ImageConsumer imageconsumer)
{
if(consumer != imageconsumer)
{
consumer = imageconsumer;
consumer.setDimensions(width,height);
consumer.setProperties(null);
consumer.setColorModel(cm);
consumer.setHints(hints);
}
consumer.setPixels(0, 0, width, height, cm, pixels, 0, width);
consumer.imageComplete(sfd);
}
public void update()
{
if(consumer != null)
startProduction(consumer);
}
public final boolean isConsumer(ImageConsumer imageconsumer)
{
return consumer==imageconsumer;
}
public final void requestTopDownLeftRightResend(ImageConsumer imageconsumer) { }
public final void removeConsumer(ImageConsumer imageconsumer) { }
}