import java.awt.*; import java.applet.*; // background shape (line/square/circle) or color or size // dot shape (line/square/circle) or color or size // rotate figure/change figure size/change figure shape // spin speed class Properties { final static int SHAPE_LINE = 0; final static int SHAPE_BOX = 1; final static int SHAPE_OVAL = 2; final static int SHAPE_3D_BOX = 3; final static int SHAPE_MAX = 3; Color backColor_; Color color_; int dotSize_; double density_; boolean filled_; int dotShape_; public Properties() { backColor_ = Color.white; color_ = Color.gray; dotSize_ = 10; density_ = 0.8; filled_ = true; dotShape_ = SHAPE_LINE; } public Properties(Properties other) { backColor_ = other.backColor_; color_ = other.color_; dotSize_ = other.dotSize_; density_ = other.density_; filled_ = other.filled_; dotShape_ = other.dotShape_; } } abstract class Shape { Properties props_; int num_; double atX_, atY_, w_, h_; double x_[], y_[], xDotSize_[], yDotSize_[], x2_[], y2_[]; double holeLastX_, holeLastY_; Shape hole_; Shape(Properties p, int atX, int atY, int width, int height) { props_ = new Properties(p); w_ = width; h_ = height; atX_ = atX; atY_ = atY; hole_ = null; } void setHole(Shape hole) { hole_ = hole; } synchronized void setProperties(Properties p) { if (props_.dotSize_ != p.dotSize_ || props_.density_ != p.density_) randomize(); else if (p.dotShape_ != props_.dotShape_ && p.dotShape_ == Properties.SHAPE_LINE) fillInEndPoints(); props_ = new Properties(p); } void fillInEndPoints() { for (int i = 0; i < num_; ++i) { x2_[i] = x_[i] + xDotSize_[i]; y2_[i] = y_[i] + yDotSize_[i]; } } synchronized void draw(Graphics g) { drawBack(g); if (hole_ != null) { double nowAtX = hole_.atX_; double nowAtY = hole_.atY_; hole_.atX_ = holeLastX_; hole_.atY_ = holeLastY_; hole_.draw(g); hole_.atX_ = nowAtX; hole_.atY_ = nowAtY; } Illusion.setBar("Filling...", 0.0); g.translate((int)atX_, (int)atY_); g.setColor(props_.color_); if (props_.dotShape_ == Properties.SHAPE_LINE) { for (int i = 0; i < num_; ++i) { if (i % 100 == 0) Illusion.setBar(null, (double)i / (double)(num_ - 1)); g.drawLine((int)x_[i], (int)y_[i], (int)(x2_[i]), (int)(y2_[i])); } } else { for (int i = 0; i < num_; ++i) { if (i % 100 == 0) Illusion.setBar(null, (double)i / (double)(num_ - 1)); int x = (int)x_[i]; int y = (int)y_[i]; int xSize = (int)xDotSize_[i]; int ySize = (int)yDotSize_[i]; if (xSize < 0) { x += xSize; xSize *= -1; } if (ySize < 0) { y += ySize; ySize *= -1; } if (props_.filled_) { switch (props_.dotShape_) { case Properties.SHAPE_BOX: g.fillRect(x, y, xSize, ySize); break; case Properties.SHAPE_OVAL: g.fillOval(x, y, xSize, ySize); break; case Properties.SHAPE_3D_BOX: g.fill3DRect(x, y, xSize, ySize, true); break; } } else { switch (props_.dotShape_) { case Properties.SHAPE_BOX: g.drawRect(x, y, xSize, ySize); break; case Properties.SHAPE_OVAL: g.drawOval(x, y, xSize, ySize); break; case Properties.SHAPE_3D_BOX: g.draw3DRect(x, y, xSize, ySize, true); break; } } } } Illusion.setBar(null, 1.0); g.translate((int)-atX_, (int)-atY_); Illusion.setBar(null, 0.0); } int countNumItems(double area, double size, double density) { return (int)(area * density * 6.0 / (size * size)); } synchronized void rotate(double angle) { double sin = Math.sin(angle); for (int i = 0; i < num_; ++i) { x_[i] = x_[i] - y_[i] * sin; y_[i] = y_[i] + x_[i] * sin; } if (props_.dotShape_ == Properties.SHAPE_LINE) { for (int i = 0; i < num_; ++i) { x2_[i] = x2_[i] - y2_[i] * sin; y2_[i] = y2_[i] + x2_[i] * sin; } } } synchronized void translate(double addX, double addY) { atX_ += addX; atY_ += addY; } void moveTo(int x, int y) { atX_ = x; atY_ = y; } void randomize() { if (hole_ != null) randomizeMinusShape(); else randomizeNormal(); if (props_.dotShape_ == Properties.SHAPE_LINE) fillInEndPoints(); } void randomizeNormal() { double size = props_.dotSize_; num_ = countNumItems(getArea(), size, props_.density_); size *= 2.0; x_ = new double[num_]; y_ = new double[num_]; xDotSize_ = new double[num_]; yDotSize_ = new double[num_]; x2_ = new double[num_]; y2_ = new double[num_]; Illusion.setBar("Picking...", 0.0); for (int i = 0; i < num_; ++i) { if (i % 100 == 0) Illusion.setBar(null, (double)i / (double)(num_ - 1)); double x, y; do { x = w_ * (Math.random() - 0.5); y = h_ * (Math.random() - 0.5); } while (!insideLocal(x, y)); x_[i] = x; y_[i] = y; xDotSize_[i] = size * (Math.random() - 0.5); yDotSize_[i] = size * (Math.random() - 0.5); } Illusion.setBar(null, 1.0); Illusion.setBar(null, 0.0); } void randomizeMinusShape() { double area = getArea() - hole_.getArea(); double halfW = w_ / 2.0; double halfH = h_ / 2.0; double size = props_.dotSize_; double density = props_.density_; num_ = countNumItems(area, size, density); size *= 2.0; x_ = new double[num_]; y_ = new double[num_]; xDotSize_ = new double[num_]; yDotSize_ = new double[num_]; x2_ = new double[num_]; y2_ = new double[num_]; holeLastX_ = hole_.atX_; holeLastY_ = hole_.atY_; Illusion.setBar("Picking...", 0.0); for (int i = 0; i < num_; ++i) { if (i % 100 == 0) Illusion.setBar(null, (double)i / (double)(num_ - 1)); double x, y; do { x = w_ * Math.random(); y = h_ * Math.random(); } while (hole_.insideGlobal(x, y)); x_[i] = x - halfW; y_[i] = y - halfH; xDotSize_[i] = size * (Math.random() - 0.5); yDotSize_[i] = size * (Math.random() - 0.5); } Illusion.setBar(null, 0.0); } abstract double getArea(); abstract boolean insideGlobal(double x, double y); abstract boolean insideLocal(double x, double y); abstract void drawBack(Graphics g); } class Box extends Shape { Box(Properties p, int atX, int atY, int width, int height) { super(p, atX, atY, width, height); } double getArea() { return w_ * h_; } boolean insideGlobal(double x, double y) { double diffX = x - (atX_ - w_ / 2); double diffY = y - (atY_ - h_ / 2); return (diffX >= 0 && diffX < w_ && diffY >= 0 && diffY < h_); } boolean insideLocal(double x, double y) { return true; } void drawBack(Graphics g) { g.setColor(props_.backColor_); g.fillRect((int)(atX_ + -w_ / 2), (int)(atY_ + -h_ / 2), (int)w_, (int)h_); } } class Circle extends Shape { double radius_; double radSq_; Circle(Properties p, int atX, int atY, int radius) { super(p, atX, atY, radius * 2, radius * 2); radius_ = radius; radSq_ = radius_ * radius_; } double getArea() { return (int)(Math.PI * radSq_); } boolean insideGlobal(double x, double y) { x -= atX_; y -= atY_; return (x * x + y * y < radSq_); } boolean insideLocal(double x, double y) { return (x * x + y * y < radSq_); } void drawBack(Graphics g) { g.setColor(props_.backColor_); g.fillOval((int)(atX_ + -w_ / 2), (int)(atY_ + -h_ / 2), (int)w_, (int)h_); } } public class Illusion extends Applet implements Runnable { final static int BAR_HEIGHT = 21; final static int BAR_WIDTH = 150; boolean translate_ = false, rotate_ = false; double addX_ = 2.0, addY_ = 2.0; int delay_ = 50; int foreSize_ = 70; int width_, height_; Image image_; Graphics offscreen_; boolean updateBackground_ = false; Properties props_; Image background_; Graphics backG_; Shape foreShape_; Shape backShape_; boolean startAfterUpdate_ = false; Thread ticker_; AMDProgressBarEmbed bar_; static boolean showBar_ = false; static Illusion instance_; public void init() { instance_ = this; props_ = new Properties(); bar_ = new AMDProgressBarEmbed(this); } public void start() { requestFocus(); } public void stop() { stopTicker(); } public void run() { long time = System.currentTimeMillis(); while (true) { if (rotate_) foreShape_.rotate(0.02); if (translate_) { double newX = foreShape_.atX_ + addX_ - foreShape_.w_ / 2; double newY = foreShape_.atY_ + addY_ - foreShape_.h_ / 2; if (newX < 0) addX_ *= -1; else { double maxX = newX + foreShape_.w_; if (maxX > width_) addX_ *= -1; } if (newY < 0) addY_ *= -1; else { double maxY = newY + foreShape_.h_; if (maxY > height_) addY_ *= -1; } foreShape_.translate(addX_, addY_); } repaint(); try { time += delay_; long toSleep = time - System.currentTimeMillis(); if (toSleep > 0) Thread.sleep(toSleep); else { Thread.sleep(delay_); time = System.currentTimeMillis(); } } catch (InterruptedException e) { ; } } } public static void setBar(String text, double percent) { if (showBar_) { if (text != null) instance_.bar_.setText(text); instance_.bar_.setPercent(percent); instance_.bar_.paint(instance_.getGraphics()); } } public void update(Graphics g) { paint(g); } public void paint(Graphics g) { if (image_ == null) { width_ = size().width; height_ = size().height; image_ = createImage(width_, height_); offscreen_ = image_.getGraphics(); bar_.reshape((width_ - BAR_WIDTH) / 2, (height_ - BAR_HEIGHT) / 2, BAR_WIDTH, BAR_HEIGHT); background_ = createImage(width_, height_); backG_ = background_.getGraphics(); backShape_ = new Box(props_, width_ / 2, height_ / 2, width_, height_); foreShape_ = new Circle(props_, 0, 0, foreSize_ / 2); backShape_.setHole(foreShape_); randomize(); } if (updateBackground_) updateBackground(); offscreen_.drawImage(background_, 0, 0, null); foreShape_.draw(offscreen_); g.drawImage(image_, 0, 0, null); } void updateBackground() { showBar_ = true; backG_.setColor(Color.white); backG_.fillRect(0, 0, width_, height_); backShape_.draw(backG_); updateBackground_ = false; showBar_ = false; if (startAfterUpdate_) { startTicker(); startAfterUpdate_ = false; } } void randomize() { if (ticker_ != null) { stopTicker(); startAfterUpdate_ = true; } showBar_ = true; int atX = (int)(Math.random() * (double)(width_ - foreSize_ * 2)) + foreSize_; int atY = (int)(Math.random() * (double)(height_ - foreSize_ * 2)) + foreSize_; foreShape_.moveTo(atX, atY); foreShape_.randomize(); backShape_.randomize(); updateBackground_ = true; repaint(); showBar_ = false; } public boolean mouseDown(Event e, int x, int y) { if (ticker_ == null) { if (!rotate_ && !translate_) translate_ = true; startTicker(); } else { rotate_ = false; translate_ = false; stopTicker(); } return true; } void startTicker() { if (ticker_ == null) { ticker_ = new Thread(this); ticker_.start(); } } void stopTicker() { if (ticker_ != null) { ticker_.stop(); ticker_ = null; } } void updateProps() { if (ticker_ != null) { stopTicker(); startAfterUpdate_ = true; } showBar_ = true; foreShape_.setProperties(props_); backShape_.setProperties(props_); updateBackground_ = true; repaint(); showBar_ = false; } public boolean keyDown(Event e, int key) { key = Character.toUpperCase((char)key); switch (key) { // re-randomize case ' ': randomize(); break; case 'R': if (!rotate_ && !translate_) startTicker(); rotate_ = !rotate_; if (!rotate_ && !translate_) stopTicker(); break; case 'T': if (!rotate_ && !translate_) startTicker(); translate_ = !translate_; if (!rotate_ && !translate_) stopTicker(); break; case '>': case '<': double newDense = props_.density_ + ((key == '<') ? -0.1 : 0.1); if (newDense > 0.9) newDense = 0.9; else if (newDense < 0.2) newDense = 0.2; props_.density_ = newDense; updateProps(); break; case 'D': ++props_.dotShape_; if (props_.dotShape_ > Properties.SHAPE_MAX) props_.dotShape_ = 0; updateProps(); break; case 'S': if (foreShape_ instanceof Circle) foreShape_ = new Box(props_, 0, 0, foreSize_, foreSize_); else foreShape_ = new Circle(props_, 0, 0, foreSize_ / 2); backShape_.setHole(foreShape_); randomize(); break; case 'C': props_.color_ = new Color((int)(Math.random() * 256), (int)(Math.random() * 256), (int)(Math.random() * 256)); updateProps(); break; case 'F': props_.filled_ = !props_.filled_; updateProps(); break; } return true; } }