I wanted to get my head wrapped around GraphicsPath and IGraphicData classes and figured a demo shown at last year’s Adobe Max would be a great piece to remake.
The concept was pretty simple: Click down anywhere and start drawing when the mouse moves. When the mouse is released, the shape drawn should move in the manner and speed it was drawn.
So drawing the shape and storing the necessary information is pretty easy. First we need some Arrays to hold the information:
shapes = new Vector.<Shape>();
commands = new Vector.<Vector.<IGraphicsData>>();
thicknesses = new Vector.<uint>();
colors = new Vector.<uint>();
When we hear a mouse down event, create new shape and add it to the stage, get a random thickness, random color, and store those:
lineStyle = new GraphicsStroke(thickness);
lineStyle.fill = new GraphicsSolidFill(color);
currentPath = new GraphicsPath();
currentPath.moveTo(x, y);
commands[shapeIndex].push(currentPath);
And on mouse move, we just do a lineTo method on the current shape created. Now the fun part, getting the shapes to redraw themselves.
On enter frame (or a timer if you prefer to control the speed) loop through every shape that was created. First thing we want to do is pull out the initial moveTo command since the drawing will start at a new position:
var path : GraphicsPath = GraphicsPath(commands[i][0]);
var moveCommand : uint = path.commands.shift();
Since we know we only have one Graphics path per shape, we can safely use commands[i][0]. Next, we can discard the coordinates of that moveTo command since we’re going to change them:
path.data.shift();
path.data.shift();
Next, lets get the first draw command, replace the moveTo command, and add in the next draw:
var drawCommand : uint = path.commands.shift();
var drawPosition : Point = new Point(path.data.shift(), path.data.shift());
var nextPosition : Point = new Point(path.data[0], path.data[1]);
var movePosition : Point = drawPosition.clone();
var dataLen : uint = path.data.length;
var lDrawPosition : Point = new Point(path.data[dataLen - 2], path.data[dataLen - 1]);
drawPosition = lDrawPosition.add(nextPosition.subtract(drawPosition));
And finally, push/shift the data/draw commands and tell our shape to redraw:
path.commands.unshift(moveCommand);
path.data.unshift(movePosition.y);
path.data.unshift(movePosition.x);
path.commands.push(drawCommand);
path.data.push(drawPosition.x);
path.data.push(drawPosition.y);
var shape:Shape = shapes[i];
shape.graphics.clear();
shape.graphics.lineStyle(thicknesses[i], colors[i]);
shape.graphics.drawGraphicsData(commands[i]);
So that works great, but we have an issue; once our shape draws past the stage width/height, its just going to go out of our visibility. There has to be something that gets the shape to wrap within the stage boundaries. We can add some conditions to modify the position of the next draw command, but then we will have a line drawn from one edge of the stage to the other:

To fix that up, we can add another moveTo command whenever we have to wrap a draw command. But then every time we add a wrap there is going to be an extra move command sitting around, and when there is no wrap, this move command will cause breaks in the drawing:
Removing those extra move commands is actually a tough one. We could loop through EACH command per shape and see if its an extra move command, but then that is pretty costly processing wise when you start to have many shapes on the stage. Beyond that, its difficult to measure if the extra move command isn’t extra, meaning if its really needed to handle the wrapping. So a temporary fix would be to clean up extra move commands on the trail of the shape. This would happen when we move the starting placement move command, and check if the next command is also a move:
while (drawCommand == moveCommand)
{
drawCommand = path.commands.shift();
drawPosition = new Point(path.data.shift(), path.data.shift());
nextPosition = new Point(path.data[0], path.data[1]);
}
And viola! This won’t clean up all the breaks in the drawing, but will prevent them from building up.
Here is the source code. Here is the working swf:
[kml_flashembed publishmethod="static" fversion="10.0.0" movie="http://labs.blitzagency.com/wp-content/uploads/2010/02/main.swf" width="450" height="339" targetclass="flashmovie"]
[/kml_flashembed]


{ 3 comments… read them below or add one }
Hey the swf at the bottom doesn’t work!
Very nice effect, thanks for sharing
Patrick – There was a bug when you clicked and didn’t drag. Should be fixed and source code is updated.