I came up with this method at work, and it seems pretty useful to me so I thought I'd share it.
It's basically a way of drawing a free hand line, but keeping all the points in that line at even distances apart.
Code:
int frame_count, mouse_count;
boolean mouse_pressed;
Paint paint;
void setup(){
size(400, 400);
paint = new Paint(10, 20);
smooth();
}
void draw(){
background(255);
paint.main();
frame_count++;
}
void mousePressed(){
mouse_count = frame_count;
mouse_pressed = true;
}
void mouseReleased(){
mouse_pressed = false;
}
class Line{
PVector a, b;
float vx, vy, dx, dy, length;
Line(PVector a, PVector b){
this.a = a;
this.b = b;
updateLine();
}
void updateLine(){
vx = b.x - a.x;
vy = b.y - a.y;
length = sqrt(vx * vx + vy * vy);
if(length > 0){
dx = vx/length;
dy = vy/length;
}
else {
dx = 0.0f;
dy = 0.0f;
}
}
void draw(){
line(a.x, a.y, b.x, b.y);
}
}
public class Paint {
ArrayList lines, dots;
PVector last;
int length;
float spacing;
Paint(int spacing, int length){
lines = new ArrayList();
dots = new ArrayList();
last = null;
this.spacing = spacing;
this.length = length;
}
// Adds dots inbetween dots that are too far apart
void interpolate(PVector a, PVector b) {
Line line = new Line(a, b);
// the number of segments we will divide the gap into
int denominator = (int)(line.length / spacing);
// the distance between each new dot (note that it will be a little above spacing
// this means it'll look slightly uneven - but no bunching and we get fast results)
float step = line.length / denominator;
for(float n = step; n < line.length; n += step) {
// hack to deal with flash's bullshit floating point math
// not sure if java does this too, but I'm playing safe
if(line.length - n < step * 0.5) break;
dots.add(new PVector(a.x + line.dx * n, a.y + line.dy * n));
}
}
// Returns true if (x0,y0) is less than length from (x1,y1)
boolean proximity(float x0, float y0, float x1, float y1, float length){
return (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0) <= length * length;
}
void main() {
int i, j;
// saves me putting this function in mousePressed()
if(mouse_count == frame_count) {
clear();
}
// painting routine
if(mouse_pressed){
float x = mouseX;
float y = mouseY;
PVector next = null;
if(last == null){
// draw the first dot
last = new PVector(x, y);
dots.add(last);
} else {
// add new dots, but only if they're further than 'spacing' apart
if(!proximity(x, y, last.x, last.y, spacing)){
next = new PVector(x, y);
}
}
if(next != null) {
// dots that are too far apart we break up
if(!proximity(last.x, last.y, next.x, next.y, spacing * 2)) {
interpolate(last, next);
}
dots.add(next);
// turn those dots into lines
while(dots.size() > 1) {
last = (PVector)dots.get(1);
lines.add(new Line((PVector)dots.remove(0), last));
}
// limit the length of the whole chain
while(lines.size() >= length) lines.remove(0);
}
}
draw();
}
// clears the paint from the screen and memory
void clear() {
lines = new ArrayList();
dots = new ArrayList();
last = null;
}
void draw(){
for(int i = 0; i < lines.size(); i++){
Line temp = (Line)lines.get(i);
temp.draw();
ellipse(temp.a.x, temp.a.y, 5, 5);
}
}
}