![]() |
![]() |
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).
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.
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.
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.
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.
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";
}
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();
}
}
Discussed on this page:
allow user to draw shapes by dragging mouse or creating bezier curves, save as shared object, output SVG, output shared object equivalent text, reduce points in drawing
Files:
as files to create sample shown at left
(free download)
A list of all files currently available at the site may be viewed here.