AS Reference  :  Notes Index  :  Resources  :  About/Contact  :  Downloads

Two ways to create and save drawings in a Flash movie


The onMouseMove Shape Constructor and the Quadratic Bezier Shape Constructor are experiments in different ways to save a shape drawn by a user in a Flash application. The OMSC allows a user to drag the mouse around to define a shape. The QBSC works more like a pen tool, allowing a user to click and drag endpoints and control points to define curves precisely.

In both routines, two fields are generated while the shape is being drawn: an SVG field (the contents of which may be cut and pasted to a file to produce the same shape via an SVG viewer), and a Flash "shape object" field (more below). The latter is a text representation of an object that is actually created in code and which may be saved locally (via Flash's shared objects) to the user's machine by clicking Save (and retrieved by clicking Retrieve).

What's the difference between those two methods?

The OMSC allows a user to draw a shape in a natural way, as though holding a pencil or paintbrush (or a clunky bar of soap if you don't have a wacom, poor you :) The downside of the routine is that all of the points collected during the user's mouseMoves must be saved to define the shape, the number of which can add up quickly. Also, it can look a bit rough if the mouse/pen is moved too quickly (but that can be an upside as well as a downside, depending on your intended outcome). The QBSC, on the other hand, saves only the minimum information needed to define each part of the shape (either a line or a quadratic bezier segment), but is rather awkward for a user to have to draw with. Ideally, these two methods could be combined, which I hope to work on if I ever have time to pursue curve-fitting...

Intro and link to code for OMSC

In the OMSC, a point movieclip from the library is attached every time the user drags the mouse (after pressing it down in the first place, of course). This is done via an onMouseMove function within an onMouseDown function. Additionally, a connector movieclip keeps a running line between each of the points -- that is what is used to define the shape object that can then be saved. The shape object for the OMSC is a little more complicated than it really needs to be: each point is defined by an object {p:{x:xcoord, y:ycoord}} instead of just the coordinates themselves, because I want it to be compatible with the QBSC, in case I ever manage to combine them. While the points are being created onstage, output textfields are also being created with SVG output (one M point, followed by as many L points as needed; see below or the SVG Path tag spec for more on SVG format) and the shape object representation.

What is a quadratic bezier curve?

Quadratic bezier curves are curves described by two endpoints and a control point. They are the curves that are used in the Flash drawing API (unlike other drawing applications, which may use cubic bezier curves, described by two endpoints and two control points). A single curve segment is defined by an initial "moveTo" point, a final "curveTo" endpoint, and a control point which defines the shape of the curve. Each additional curve segment in the same shape is defined by its own endpoint and its own control point.

How does that relate to SVG?

In the SVG spec, a shape is defined with a path tag (<path>). The path tag contains a d attribute which holds letter commands for the drawing cursor. Among these are M for moveTo, Q for "quadratic bezier curveTo", C for "cubic bezier curveTo", L for lineTo, and z for "close this shape by drawing a line back to the starting point". Thus, an SVG file makes a convenient container for a shape description, or for many shape descriptions (each one contained in its own path tag).

In the Quadratic Bezier Shape Constructor, you can see the svg file being built as the shape is drawn. An initial point results in a moveTo (M) being added to the file, subsequent line points are represented by lineTo's (L). If you change the line to a curve (by dragging its control point), the L changes to a Q, and 4 numbers follow it instead of 2. The first two numbers represent the x and y coordinates of the control point, and the third and fourth represent the endpoint. No actual SVG file is created in this example (you need a serverside script to do that), just a textfield. But you can easily copy the contents of the SVG field (highlight all, right-click, Copy), paste into a file with, eg, Notepad, and save as xx.svg. Then if you have an SVG plugin installed in your browser (such as Adobe's SVG Viewer), you can view the shape from a browser (File, Open, xx.svg) and see that it's identical to the shape you drew in Flash.

So what is a shape object?

An SVG file provides one way of saving a drawn shape. It's text-based and therefore very readable and also sharable between applications. But a more efficient way, if the shape is to be used only by a Flash application, is with an object that Flash can understand natively (without having to parse and convert any text). So in addition to the SVG output, I also show what I call a Flash shape object being created. The text shown on the lower right of the QBSC is just a text representation of the shape object; the actual object is created in code when a user clicks "Save".

The shape object is a collection of sub-objects, including a stroke object (with properties color, width and alpha), a fill object (color and alpha properties), a p object (with x and y properties representing the coordinates of the initial moveTo), and a pts array of objects. Each element of the pts array describes one point within the shape: either a lineTo, or a curveTo, with its representative coordinates saved as part of the element. They are stored in sequential order, so that Flash can then read them back in that same order and recreate the shape as it was originally drawn. Altogether, one shape object defines a complete shape drawn with lines and/or quadratic bezier segments.

How is the shape object saved?

Flash MX provides a nice way of saving data on a user's machine -- like a cookie, but specific to Flash. Each domain gets its own shared-object directory on the user's machine, and the user may specify how much space to allot to the directory, or whether to even allow it to exist at all (via right-clicking on a Flash app and choosing Settings, or when prompted by an application that needs more space than is currently allotted). If you run the QBSC and choose Save (above the shape object listing), you should see a file called QBShape.sol in directory qbsc.swf under Documents and Settings/loginname/Application Data/Macromedia/Flash Player/flashcreations.com on your pc or Users:username:Library:Preferences:Macromedia:Flash Player:flashcreations.com:qbsc.swf:QBShape.sol on your mac (thanks Arie van Boxel for passing on the latter). (Note that in Flash 7 and higher, shared objects are saved in an unmarked location, not in the directories described above).

QBShape is the object itself, and this is the code I used to create and save it:

    var shapeObj = {
    stroke:{width:0, color:0, alpha:100},
    fill:{color:0, alpha:0},
    pts:[],
    p:{x:curve.pttl.p00._x, y:curve.pttl.p00._y} };
    // curve.pttl = timeline where the point movieclips exist
    for (var i=1; i<curve.pts.length; i++) {
        var i2 = i<10 ? "0"+i : i;
        thisp = curve.pttl["p"+i2];
        thisc = curve.pttl["c"+i2];
        if (curve.pts[i].isLine) {
        	shapeObj.pts.push({p:{x:thisp._x, y:thisp._y}});
        } else {
        	shapeObj.pts.push({p:{x:thisp._x, y:thisp._y}, 
              c:{x:thisc._x, y:thisc._y}});
        }
    }
    // if user closed curve, add last segment (index = 0)
    // (more code here; not really relevant, so removed)
	
    // save the object as a shared object
    soQBDrawing = sharedobject.getLocal("QBShape" );
    soQBDrawing.data.savedValue = shapeObj;
    if (!soQBDrawing.flush()) {
        msgArea.text = "unable to save";
    } else {
        msgArea.text = "shape with " + shapeObj.pts.length 
         + " points saved";
    }

How is the shape reconstructed in Flash?

Getting the shape back out and drawn in Flash just requires the opposite -- pulling QBShape out of the .sol file, and drawing a movieclip based on its contents:

    var soQBDrawing = sharedobject.getLocal("QBShape" );
    var shapeObj = soQBDrawing.data.savedValue;
    var ox = drawStage._x;
    var oy = drawStage._y;
    drawStage.createEmptyMovieClip("retrieved",1);
    with (drawStage.retrieved) {
        lineStyle(shapeObj.stroke.width, shapeObj.stroke.color, 
		  shapeObj.stroke.alpha);
        if (shapeObj.fill.alpha!=0) {
            beginFill(shapeObj.fill.color, shapeObj.fill.alpha);
        }
        moveTo(shapeObj.p.x-ox, shapeObj.p.y-oy);
        for (var i=0; i<shapeObj.pts.length; i++) {
            if (shapeObj.pts[i].c == null) {
                lineTo(shapeObj.pts[i].p.x-ox, shapeObj.pts[i].p.y-oy);
            } else {
                curveTo(shapeObj.pts[i].c.x-ox, shapeObj.pts[i].c.y-oy,
                  shapeObj.pts[i].p.x-ox, shapeObj.pts[i].p.y-oy);
            }
        }
        if (shapeObj.fill.alpha!=0) {
            endFill();
        }
    }

Intro
Flash: What & How
Example Sites
Create
Draw, Edit Shapes
Gradients
More Drawing Tips
Import
A Sample
Animate
Frames, Keyframes
Motion Tweens
More Motion Tweens
Shape Tweens
Masks
Control
Stop/Replay
Movieclips Intro
Movieclip Reference
Site Structure 1
Slideshow Movieclip
Contact Form
Scroll Resume
Preloader
Site Structure 2
Publish
Display Options
Player Detection
Optimize
AS 2.0 Basics
Intro to Syntax
Playhead Commands
Playhead Cmds 2
Coded Tween
onEnterFrame
Intro to Classes
Declare/Assign
Comments, Trace
Simple Data Types
Arrays & Objects
Code Blocks
Operators
Beyond Buttons
Code Structure
Toggle Controls
Group of Buttons
Drag and Hit
Distort Magnifier
Scroll Text
Bee Game
Dart Shooter
Sound Control
Easing Slider
Easing Slider 2
Components Intro
Timers & Delays
Dynamic Content
Intro
Drawing API
Create Text
Attach Movieclips
Easing Slider 3
Easing Slider 4
Load jpg/swf
Sliding Viewer
Preload swf
XML
Easing Slider 5
Server Comm
LoadVars (w/ PHP)
AS - PHP Lookup
Text File
Database 1:LoadVars
Database 2:Remoting
Read from directory
AS 2.0 Classes
Intro
Math
Key
Date
Color
EventDispatcher
New Samples
Pie Chart
Event-model Emailer
Tween Sequence
Fuse Sequence
SVG in Flash
Bitmap Topo
SWF as Data Holder
Two-level Menu
Yahoo! Flash Maps
Class-based Game
ASTB Samples
Disclaimer
3D Outlines
Bounce Collide
Address Book
Save Drawings
Home  :  Notes Index  :  Resources  :  About/Contact  :  Downloads