PDA

View Full Version : Smooth Drawing


icio
12-29-2006, 07:55 PM
This method creates beziers using the points drawn.

I thought this might warrant it's own thread, for searchers' sake.

I've used two methods. One takes every point where the distance from the previous point is 50 and the other method follows a slightly more standard procedure by taking the position of the mouse on every new frame.

1: Test (http://img293.imageshack.us/img293/3245/drawingnb0.swf)
Takes points 50 units away from each other.
import flash.geom.Point;

Stage.scaleMode = "noScale";
Stage.align = "TL";
Stage.showMenu = false;

var track:MovieClip;
var section:MovieClip;
var length:Number = 30;

var points:Array = [];
var p:Point;
var drawing:Boolean = false;

function onMouseDown() {
points = [];

drawing = true;
section = _root.createEmptyMovieClip("_section", 2);
section._x = _xmouse;
section._y = _ymouse;
section.onMouseMove = function() {
this.clear();
this.lineStyle(1, 0x666666);
this.lineTo(this._xmouse, this._ymouse);
}
section.onMouseMove();

p = new Point(_xmouse, _ymouse);
points.push(p);

_root.lineStyle(1, 0xCCCCCC);
_root.moveTo(_xmouse, _ymouse);
}

function onMouseUp() {
points.push(new Point(_xmouse, _ymouse));
_root.lineTo(_xmouse, _ymouse);

drawing = false;
section.removeMovieClip();

if (!track) {
track = _root.createEmptyMovieClip("_track", 1);
}

var p1:Point, p2:Point, p3:Point, mid1:Point, mid2:Point;
var p1:Point = linearBezierPoint([points[0], points[1]], 0.5);

track.lineStyle(0, 0x000000);
track.moveTo(points[0].x, points[0].y);
track.lineTo(p1.x, p1.y);

for (var i:Number=0; i<points.length-2; i++) {
p1 = points[i];
p2 = points[i+1];
p3 = points[i+2];
mid1 = linearBezierPoint([p1, p2], 0.5);
mid2 = linearBezierPoint([p2, p3], 0.5);
track.bezier([mid1, p2, mid2], 20);
}

track.lineTo(p3.x, p3.y);
}

function onMouseMove() {
if (!drawing) return;

var dx:Number = _xmouse - p.x;
var dy:Number = _ymouse - p.y;
var d:Number = Math.sqrt(dx*dx+dy*dy);

if (d >= length) {
section._x = _xmouse;
section._y = _ymouse;
section.onMouseMove();

p = new Point(_xmouse, _ymouse);
points.push(p);

_root.lineTo(_xmouse, _ymouse);
}
}

MovieClip.prototype.linearBezierPoint = function(p:Array, t:Number):Point {
if (t < 0 || t > 1 || p.length != 2) return null;

return new Point(
p[0].x+(p[1].x-p[0].x)*t,
p[0].y+(p[1].y-p[0].y)*t
);
};

MovieClip.prototype.quadraticBezierPoint = function(p:Array, t:Number):Point {
if (t < 0 || t > 1 || p.length != 3) return null;

var ax:Number, bx:Number;
bx = 2*(p[1].x-p[0].x);
ax = p[2].x - p[0].x - bx;

var ay:Number, by:Number;
by = 2*(p[1].y - p[0].y);
ay = p[2].y - p[0].y - by;

var t2:Number = t*t;

return new Point(
ax*t2 + bx*t + p[0].x,
ay*t2 + by*t + p[0].y
);
};

MovieClip.prototype.bezier = function (p:Array, segments:Number) {
if (segments < 1) return;

var func:Function;
if (p.length < 2) {
return;
} else if (p.length == 2) {
func = this.linearBezierPoint;
} else if (p.length == 3) {
func = this.quadraticBezierPoint;
} else if (p.length == 4) {
func = this.cubicBezierPoint;
} else {
return;
}

var dt:Number = 1/segments;
var s:Point = func(p, 0);
this.moveTo(s.x, s.y);

for (var i:Number=1; i<=segments; i++) {
s = func(p, i*dt);
this.lineTo(s.x, s.y);
}
}

function onKeyDown() {
if (Key.getCode() == Key.SPACE) {
_root.clear();
track.clear();
_root.lineStyle(1, 0xCCCCCC);
track.lineStyle(0, 0x000000);
}
}
Key.addListener(this);

2: Test (http://img112.imageshack.us/img112/3709/drawing2lu1.swf)
A slightly more conventional method, takes points where the mouse on every new frame.
import flash.geom.Point;

Stage.scaleMode = "noScale";
Stage.align = "TL";
Stage.showMenu = false;

var track:MovieClip;
var section:MovieClip;
var length:Number = 50;

var points:Array = [];
var p:Point;
var drawing:Boolean = false;

function onMouseDown() {
points = [];
drawing = true;

p = new Point(_xmouse, _ymouse);
points.push(p);

_root.lineStyle(1, 0xCCCCCC);
_root.moveTo(_xmouse, _ymouse);
}

function onMouseUp() {
points.push(new Point(_xmouse, _ymouse));
_root.lineTo(_xmouse, _ymouse);

drawing = false;
section.removeMovieClip();

if (!track) {
track = _root.createEmptyMovieClip("_track", 1);
}

var p1:Point, p2:Point, p3:Point, mid1:Point, mid2:Point;
var p1:Point = linearBezierPoint([points[0], points[1]], 0.5);

track.lineStyle(0, 0x000000);
track.moveTo(points[0].x, points[0].y);
track.lineTo(p1.x, p1.y);

for (var i:Number=0; i<points.length-2; i++) {
p1 = points[i];
p2 = points[i+1];
p3 = points[i+2];
mid1 = linearBezierPoint([p1, p2], 0.5);
mid2 = linearBezierPoint([p2, p3], 0.5);
track.bezier([mid1, p2, mid2], 20);
}

track.lineTo(p3.x, p3.y);
}

function onEnterFrame() {
if (!drawing) return;

p = new Point(_xmouse, _ymouse);
points.push(p);

_root.lineTo(_xmouse, _ymouse);
}

MovieClip.prototype.linearBezierPoint = function(p:Array, t:Number):Point {
if (t < 0 || t > 1 || p.length != 2) return null;

return new Point(
p[0].x+(p[1].x-p[0].x)*t,
p[0].y+(p[1].y-p[0].y)*t
);
};

MovieClip.prototype.quadraticBezierPoint = function(p:Array, t:Number):Point {
if (t < 0 || t > 1 || p.length != 3) return null;

var ax:Number, bx:Number;
bx = 2*(p[1].x-p[0].x);
ax = p[2].x - p[0].x - bx;

var ay:Number, by:Number;
by = 2*(p[1].y - p[0].y);
ay = p[2].y - p[0].y - by;

var t2:Number = t*t;

return new Point(
ax*t2 + bx*t + p[0].x,
ay*t2 + by*t + p[0].y
);
};

MovieClip.prototype.bezier = function (p:Array, segments:Number) {
if (segments < 1) return;

var func:Function;
if (p.length < 2) {
return;
} else if (p.length == 2) {
func = this.linearBezierPoint;
} else if (p.length == 3) {
func = this.quadraticBezierPoint;
} else if (p.length == 4) {
func = this.cubicBezierPoint;
} else {
return;
}

var dt:Number = 1/segments;
var s:Point = func(p, 0);
this.moveTo(s.x, s.y);

for (var i:Number=1; i<=segments; i++) {
s = func(p, i*dt);
this.lineTo(s.x, s.y);
}
}

function onKeyDown() {
if (Key.getCode() == Key.SPACE) {
_root.clear();
track.clear();
_root.lineStyle(1, 0xCCCCCC);
track.lineStyle(0, 0x000000);
}
}
Key.addListener(this);

Enjoy,
Hope this helps somebody :thumb:

restlessdesign
01-04-2007, 04:22 PM
Very cool! I'm sure I will have use for this somewhere down the road—thanks! :)