import java.awt.*; import java.awt.event.*; import java.util.Random; /** * InformalLineGenerator - implements a drawing method for creating a * wavy looking line. * *

* See * Creating Informal Looking Interfaces (June 97) for a discussion. * *

* Copyright Jonathan Meyer, April 2002. */ public class InformalLineGenerator { int numVertices = 10; double numCycles = 1; double scale = 1/10.0; int maxThickness = 30; /** * Returns the number of extra vertices to add to a line segment. * The default is 10. */ public int getNumVertices() { return numVertices; } /** * Sets the number of extra vertices to add to a line segment. */ public void setNumVertices(int n) { numVertices = n; } /** * Returns a number indicating how much of the noise curve is sampled for * each line segment. If this is 1 (the default), then one cycle of noise is * added to each line segment. Increasing this number increases the frequency * of the noise in the line. Decreasing this number reduces the frequency * of noise in the line. */ public double getNumCycles() { return numCycles; } /** * Sets a parameter indicating how much of the noise curve is sampled for * each line segment. */ public void setNumCycles(double n) { numCycles = n; } /** * Returns the scale of the noise that is added to the * line segment. This value is multiplied by the length of the line to * get the actual amplitude of the noise to use. Increasing this number * increases the amplitude of noise added to the line segment.

* * The default scale is 1/10. i.e. a 10 pixel-long line can have up to * 1 pixel's worth of deviation introduced by noise. */ public double getScale() { return scale; } /** * Sets the scale of the noise that is added to the line segment. */ public void setScale(double s) { scale = s; } /** * Returns the maximum distance (in pixels) that the noisy vertices can * be from the straight line segment. */ public int getMaxThickness() { return maxThickness; } /** * Sets the maximum distance (in pixels) that the noisy vertices can * be from the straight line segment. */ public void setMaxThickness(int a) { maxThickness = a; } public void drawLine(Graphics g, double noiseStartPosition, int startX, int startY, int endX, int endY) { int lx = endX - startX; int ly = endY - startY; int l = lx * lx + ly * ly; // dot product if (l > 0) { double pos = noiseStartPosition; double step = (double)numCycles/numVertices; double wx, wy; if ((l * scale*scale) > (maxThickness*maxThickness)) { // Exceeded max thickness double invLen = 1.0/Math.sqrt(l); wx = -ly*invLen*maxThickness; wy = lx*invLen*maxThickness; } else { wx = -ly * scale; wy = lx * scale; } int x1 = startX, y1 = startY; for (int i = 1; i < numVertices; i++) { double t = i/(double)numVertices; double noiseVal = noise(pos); pos += step; int x2 = (int)(lerp(t, startX, endX) + noiseVal * wx); int y2 = (int)(lerp(t, startY, endY) + noiseVal * wy); g.drawLine(x1, y1, x2, y2); x1 = x2; y1 = y2; } g.drawLine(x1, y1, endX, endY); } } public void drawJitteredLine(Graphics g, double noiseStartPosition, int startX, int startY, int endX, int endY) { int lx = endX - startX; int ly = endY - startY; int l = lx * lx + ly * ly; if (l > 0) { double pos = noiseStartPosition; double step = (double)numCycles/numVertices; double wx, wy; if ((l * scale*scale) > (maxThickness*maxThickness)) { // Exceeded max thickness double invLen = 1.0/Math.sqrt(l); wx = -ly*invLen*maxThickness; wy = lx*invLen*maxThickness; } else { wx = -ly * scale; wy = lx * scale; } int x1 = startX, y1 = startY; for (int i = 1; i < numVertices; i++) { double t = i/(double)numVertices; double noiseVal = noise(pos); pos += step; int x2 = (int)(lerp(t, startX, endX) + noiseVal * wx); int y2 = (int)(lerp(t, startY, endY) + noiseVal * wy); // Add jitter to x1, y1 t = (i-1)/(double)numVertices; noiseVal = noise(pos); pos += step; if (noiseVal < 0) noiseVal = -noiseVal; t = t-noiseVal; if (t < -0.01) t=-0.01; x1 = (int)(lerp(t, startX, endX) + noiseVal * wx); y1 = (int)(lerp(t, startY, endY) + noiseVal * wy); g.drawLine(x1, y1, x2, y2); x1 = x2; y1 = y2; } g.drawLine(x1, y1, endX, endY); } } /* 1-D Perlin Noise */ static private final int B = 0x100; static private final int BM = 0xff; static private final int N = 0x1000; static private final int NP = 12; static private final int NM = 0xfff; static private int p[] = new int[B + B +2]; static private double g3[][] = new double[B + B + 2][3]; static private double g2[][] = new double[B + B + 2][2]; static private double g1[] = new double[B + B +2]; static private double s_curve(double t) { return(t*t*(3-2*t)); } public static double noise(double arg) { double rx0, rx1, sx, t, u, v; int bx0, bx1; t = arg + N; bx0 = ((int)t)&BM; bx1 = (bx0+1)&BM; rx0 = t - (int) t; rx1 = rx0 -1; sx = s_curve(rx0); u = rx0 * g1[p[bx0]]; v = rx1 * g1[p[bx1]]; return lerp(sx, u, v); } static { int i, j, k; double t; Random r = new Random(); for(i=0; i0) { k = p[i]; j = (int)(r.nextLong()&BM); p[i] = p[j]; p[j] = k; } for( i = 0; i