|
|
A spirograph can be best defined as follows:
A Spirograph is formed by rolling a circle inside or outside of another circle. The pen is placed at any point on the rolling circle. If the radius of fixed circle is R, the radius of moving circle is r, and the offset of the pen point in the moving circle is O, then the equation of the resulting curve is defined by:
x = (R+r)*cos(t) - (r+O)*cos(((R+r)/r)*t)
y = (R+r)*sin(t) - (r+O)*sin(((R+r)/r)*t)
Now that we know this it is time to investigate further and see if we can wrap-up this algorithm in a little java applet.
You can see the result below. After playing with it for a little bit I decided to change the algorithm a little so it would randomly define the parameters in the formula.
If you click on the image the applet randomly change to a new series of images.
Finnally here is the java source to make the spirograph:
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;
import java.applet.*;
/*
Copyleft 2004-2006 Arne Diegenbach
Version 1.0 made for various pizza sites
Version 1.01 hacked together for http://www.unna.nl
Version 1.02 hacked together for http://www.robota.nl
Play with it, trash it or whatever. If it doesn't work I didn't make it.
*/
class GraphPanel extends Panel implements Runnable {
Inspiro graph;
Thread relaxer;
GraphPanel(Inspiro graph) {
this.graph = graph;
}
public void run() {
while (true) {
try { Thread.sleep(kSpeed); } catch (Exception e) { };
relax();
}
}
synchronized void relax() {
// Dimension d = size();
repaint();
}
Image offscreen;
Dimension offscreensize;
Graphics offgraphics;
final Color lineColor = Color.blue;
final Color nodeColor = new Color(180, 180, 230);
final Color bgColor = new Color(255, 255, 255);
int kMax_R = 300;
int kMin_R = 30;
int kMax_r = 80;
int kMin_r = 20;
int kMin_off = 0;
int kMax_off = 100;
long kSpeed = 1000;
double R = 100.0;
double r = 20.0, off = 2;
int tweenMode = 1;
boolean up = true;
int state = 0;
int paintMode = 0;
double step = 1.0;
////////////////////////////////////////////////////////////////////////
public void chooseParams() {
// if (++tweenMode == 7)
{
r = (kMax_r * Math.random()) + kMin_r;
R = (kMax_R * Math.random()) + kMin_R;
off = (kMax_off * Math.random()) + kMin_off;
tweenMode = (int)(Math.random()*6)+1;
}
up = (boolean)(Math.random() > 0.5);
paintMode = (int)Math.rint(Math.random()*5);
state = 0;
step = 0.4 + Math.rint(Math.random()*10);
}
////////////////////////////////////////////////////////////////////////
public synchronized void update(Graphics g) {
Dimension d = size();
if ((offscreen == null) || (d.width != offscreensize.width) || (d.height != offscreensize.height)) {
offscreen = createImage(d.width, d.height);
offscreensize = d;
offgraphics = offscreen.getGraphics();
// offgraphics.setFont(getFont());
}
// offgraphics.setColor(getBackground());
offgraphics.setColor(bgColor);
offgraphics.fillRect(0, 0, d.width, d.height);
offgraphics.setColor(lineColor) ;
if ((tweenMode & 1) != 0) {
if (up) {
if (++r > kMax_r) {
up = false;
r--;
if (++state > 1) chooseParams();
}
}
else {
if (--r < kMin_r) {
up = true;
r++;
if (++state > 1) chooseParams();
}
}
}
if ((tweenMode & 2) != 0) {
if (up) {
if (++R > kMax_R) {
up = false;
R--;
if (++state > 1) chooseParams();
}
}
else {
if (--R < kMin_R) {
up = true;
R++;
if (++state > 1) chooseParams();
}
}
}
if ((tweenMode & 4) != 0) {
if (up) {
if (++off > kMax_off) {
up = false;
off--;
if (++state > 1) chooseParams();
}
}
else {
if (--off < kMin_off) {
up = true;
off++;
if (++state > 1) chooseParams();
}
}
}
switch (tweenMode) {
case 0:
break;
case 1:
break;
case 2:
break;
}
int x,y;
double t = 0, last = 2*Math.PI*R, rad = R+r;
int scale = (int)(255.0 / last);
int mw = d.width / 2;
int mh = d.height / 2;
int lx = mw+(int)Math.rint(rad*Math.cos(t) - (r+off)*Math.cos((rad/r)*t));
int ly = mh+(int)Math.rint(rad*Math.sin(t) - (r+off)*Math.sin((rad/r)*t));
int xPoints[] = new int[5];
int yPoints[] = new int[5];
int rnd1 = (int)Math.rint(Math.random()*255);
int rnd2 = (int)Math.rint(Math.random()*255);
for (t = step; t < last; t += step) {
int c = (int)(scale*t);
int rnd3 = (int)Math.rint(Math.random()*255);
offgraphics.setColor(new Color(rnd3,rnd1,rnd2, 128)) ;
x = mw+(int)Math.rint(rad*Math.cos(t) - (r+off)*Math.cos((rad/r)*t));
y = mh+(int)Math.rint(rad*Math.sin(t) - (r+off)*Math.sin((rad/r)*t));
if (paintMode == 1) {
int h = (int)r, w = (int)R;
offgraphics.drawOval(x,y,h,w);
}
else if (paintMode != 0) {
xPoints[0] = x;
yPoints[0] = y;
xPoints[1] = mw+(int)Math.rint(rad*Math.cos(last-t) - (r+off)*Math.cos((rad/r)*(last-t)));
yPoints[1] = y;
xPoints[2] = x;
yPoints[2] = mh+(int)Math.rint(rad*Math.sin(last-t) - (r+off)*Math.sin((rad/r)*(last-t)));
xPoints[3] = xPoints[1];
yPoints[3] = yPoints[2];
xPoints[4] = xPoints[0];
yPoints[4] = yPoints[0];
if (paintMode == 2)
offgraphics.drawPolygon(xPoints,yPoints,5);
else
offgraphics.fillPolygon(xPoints,yPoints,5);
}
else {
offgraphics.drawLine(lx,ly,x,y);
lx = x; ly = y;
}
/*
x = mw+(int)Math.rint(rad*Math.cos(t) - (r+off)*Math.cos((rad/r)*t));
y = mh+(int)Math.rint(rad*Math.sin(t) - (r+off)*Math.sin((rad/r)*t));
offgraphics.drawLine(lx,ly,x,y);
lx = x; ly = y;
t += step;
// offgraphics.setColor(Color.green) ;
offgraphics.setColor(new Color(rnd1,c,rnd2, 128)) ;
x = mw+(int)Math.rint(rad*Math.cos(t) - (r+off)*Math.cos((rad/r)*t));
y = mh+(int)Math.rint(rad*Math.sin(t) - (r+off)*Math.sin((rad/r)*t));
offgraphics.drawLine(lx,ly,x,y);
lx = x; ly = y;
t += step;
offgraphics.setColor(new Color(rnd1,rnd2,c, 128)) ;
x = mw+(int)Math.rint(rad*Math.cos(t) - (r+off)*Math.cos((rad/r)*t));
y = mh+(int)Math.rint(rad*Math.sin(t) - (r+off)*Math.sin((rad/r)*t));
offgraphics.drawLine(lx,ly,x,y);
lx = x; ly = y;
**/
}
String str = "www.robota.nl";
// offgraphics.setColor(Color.WHITE);
offgraphics.setFont(new Font("Helvetica",Font.ITALIC + Font.BOLD,16));
mw -= offgraphics.getFontMetrics().stringWidth(str) / 2;
offgraphics.drawString(str, mw, mh);
g.drawImage(offscreen, 0, 0, null);
}
public synchronized boolean mouseDown(Event evt, int x, int y) {
chooseParams();
return true;
}
public synchronized boolean mouseExit(java.awt.Event evt, int x, int y) {
return true;
}
public synchronized boolean mouseMove(java.awt.Event evt, int x, int y) {
return true;
}
public synchronized boolean mouseDrag(Event evt, int x, int y) {
return true;
}
public synchronized boolean mouseUp(Event evt, int x, int y) {
return true;
}
public void start() {
relaxer = new Thread(this);
relaxer.start();
}
public void stop() {
relaxer.stop();
}
}
public class Inspiro extends Applet {
GraphPanel panel;
TextArea dataField;
public void init() {
setLayout(new BorderLayout());
panel = new GraphPanel(this);
add("Center", panel);
if (getParameter("pizza").equals("Arne")) {
panel.kMax_R = Integer.parseInt(getParameter("Max_R"), 10);
panel.kMin_R = Integer.parseInt(getParameter("Max_R"), 10);
panel.kMax_r = Integer.parseInt(getParameter("Max_r"), 10);
panel.kMin_r = Integer.parseInt(getParameter("Max_r"), 10);
panel.kMin_off = Integer.parseInt(getParameter("Min_off"), 10);
panel.kMax_off = Integer.parseInt(getParameter("Max_off"), 10);
panel.kSpeed = Integer.parseInt(getParameter("Speed"), 1000);
}
}
public void start() {
panel.start();
}
public void stop() {
panel.stop();
}
public boolean action(Event evt, Object arg) {
return false;
}
public String getAppletInfo() {
return "Title: Inspiro\nAuthor: Arne Diegenbach ";
}
}
|