Tutorials, extensions, and source files for ActionScript, Flash, and other Adobe products.

 

Pages: 1 | 2 | 3 | 4 | 5

Groups Within Auto Shapes

So far, only paths have been created in the Auto Shape examples. These paths exist within the main Auto Shape group element (smartShape.elem, a group that contains all smartShape.elem.elements). Auto Shapes can, however, not only contain paths, but groups of paths or even groups of groups of paths.

Groups in Auto Shapes are created much like paths are using new with the Group constructor. When a group is created as an Auto Shape element, it provides that element (the group) with a new elements array where paths or other elements can be created--effectively a new "layer" in which elements can exist.

smartShape.elem.elements[0] = new Group();
smartShape.elem.elements[0].elements[0] = new Path();

Groups are useful for containing elements so that they are easily managed. This can be helpful in applying effects to multiple elements at once (just apply an effect to the group and it will be applied to all elements within that group) and for managing preview paths. If you have a shape that requires a preview shape for user interaction and has more than one path or an always changing number of paths, it will be a lot easier to keep them within a group. That way, you can draw your preview path along side that group and always know which element that preview is regardless of how many paths make up the actual shape.

Text Within Auto Shapes

The other element type that Auto Shapes support is Text. Since text is... text, and not so much a shape, it usually isn't used so much with Auto Shapes. However, text can be helpful at times and even be the basis on which some Auto Shape are used.

Text elements are created in shapes using new and the Text constructor. Doing so will create an empty text block.

smartShape.elem.elements[0] = new Text(); 

Now, the peculiar thing about text elements is that you can't edit properties within a text element's initial textRuns property (where the characters that make up the text reside, read more about Text and TextRuns objects in the Extending Fireworks PDF) and expect them to be applied. Instead, you have to reassign the entire textRuns property if you want to make changes any of its properties. This is a little awkward, but it's always been this way. What you would do instead of editing properties directly from the textRuns object is assign the current textRuns to a variable. This variable would then represent the current textRuns state of the text. Then, edits can be made to that variable and then reassigned to the original textRuns property of the text element for the changes to take effect. Additionally, using new Text() does not create a textRuns array for the textRuns object of the text (yes, there's two of them!) so you would have to take care of that as well. For example, to create a new text element in an Auto Shape with the characters "Hello World!" you would use the following.

smartShape.elem.elements[0] = new Text();
var runs = smartShape.elem.elements[0].textRuns;
runs.textRuns = [ {changedAttrs:{}, characters:"Hello World!"} ];
smartShape.elem.elements[0].textRuns = runs;

Notice that the runs variable represents the text's textRuns property but also has assigned to it the additional textRuns array (textRuns.textRuns) which ultimately contains the actual characters (characters) of the text. That variable then needs to be reassigned to the text element as its textRuns for those additions/changes to be applied to the actual text element.

An alternative to working off a copy would be creating the entire textRuns object yourself much in the same way element colors or effects are added. This lets you also include properties for other parts of the text (initialAttrs) without having to worry about working off a copy. This method is good for creating new text while the above may be preferable for editing existing text. Consider the following example.

smartShape.elem.elements[0] = new Text();
smartShape.elem.elements[0].textRuns = {
	initialAttrs:{
		alignment:"left",
		antiAliasSharpness:192,
		antiAliasStrength:64,
		baselineShift:0,
		bold:false,
		face:"Verdana",
		fillColor:"#000000"
		horizontalScale:1,
		italic:false,
		leading:1,
		leadingMode:"percentage",
		overSample:8,
		paragraphIndent:0,
		paragraphSpacingAfter:0,
		paragraphSpacingBefore:0,
		rangeKerning:0,
		size:"10pt",
		underline:false
	},
	textRuns:[
		{
			changedAttrs:{},
			characters: "Hello World!"
		}
	]
};

This is much like the previous example, it too creating text saying "Hello World!" but this manually assigns the textRuns object directly using a user defined object.

When positioning text in an Auto Shape, you don't really have nodes to set positions for as you do with paths. Text elements, instead, have unique properties for position, rawLeft and rawTop. These represent the left and top of the text where it is visible--not the top and left of the actual text selection as you might deal with in Fireworks. These values actually give you a little precision in text positioning since they accurately represent raw or pixel locations.

 

As a text-only Auto Shape example, we can create a "wavy" text shape. This Auto Shape consists only of text (which the user can edit) whose characters vary in size based on a mathematical sine wave.

Auto Shape: Text Wave [ Show/Hide Code | Download ]

In examining the code, you can see that the textRuns for the text created is modified by working off a copy stored in the variable runs in the UpdateText function which is essentially this shape's Draw. runs is altered by assigning to it runsArray which is the textRuns array containing all the characters of the text, each with a new point size to generate the wave (based on some math in the for loop using the sine function Math.sin). Also, you'll notice that the customData object (assigned as the nice short variable data) was used to store the value of the text used. This simply makes it easier to retrieve the text for the input prompt since otherwise a loop would be needed to cycle through all the characters of the current textRuns array of the textRuns object of the text.

At this point in time, minTextSize, maxTextSize, and frequency for the shape are pre-defined in the script. These would be suitable to have slider control points for. Are you up for it?

 

Bitmaps Within Auto Shapes

As mentioned before, Auto Shapes do not support bitmap elements. This doesn't, however, prevent you from adding bitmaps to your shapes altogether. To so, you just need to work around that binding stipulation. How you would do that is by using patterns.

Patterns let you add bitmap fills to path elements. Having a square path element using a similarly sized pattern fill (i.e. the size of the pattern bitmap is the same as the size of the square) provides you with what is visually the same as just having the pattern by itself as a bitmap. Since Auto Shapes can support path elements and paths can support patterns, Auto Shapes can support patterns. Since Auto Shapes can support patterns, it means Auto Shapes can support visuals that can match those of bitmaps despite their inability to support bitmaps directly.

Adding a pattern to a path in JavaScript is just a matter of altering that path's pattern object (within pathAttributes.fill). However, the caveat to using patterns in Auto Shapes is that it requires the inconvenience installing of those patterns on the users system. Given the lack of organization in the way Fireworks handles patterns, this becomes more and more of an issue as more patterns are added to those already installed, especially if the patterns used are so specific that they pertain only to an Auto Shape (it's bad enough just trying to scroll through the default patterns). Generally, it's a good idea to avoid the use of patterns when possible, or at least make the best of those you do use if you decide your Auto Shape needs to.

 

So, lets take this concept and apply it to an Auto Shape. What I have in mind is a button. Not just any ordinary button, a Mac OS X Aqua button. Before getting into the shape, though, we'll need some bitmaps for the button. I just happen to have some handy here.

These will make up the left end (or "start"), the middle, and the right end of the button shape and will be applied to similarly sized path shapes as patterns. The piecing is to allow for a scalable button whose middle can expand to match the length of the button text. Save these images in your Configuration\Patterns directory and restart Fireworks if you have it currently open. That will install the patterns so they can be recognized by Fireworks and ultimately the Auto Shape.

As far as Auto Shapes go, the code for this particular shape isn't particularly difficult since it only consists of only 3 rectangular shapes. It will, however, make good use of not only paths, but patterns and even text elements.

Auto Shape: Aqua Button [ Show/Hide Code | Download ]

What this does is first creates the text and the path rectangles making up the shapes to use the patterns. It is important the text is created as the first element since it needs to be on top of the other paths; the first elements in the elements array are on top of those elements added after it. Notice that the paths were created by extending the lengths of the elements array in smartShape.elem. This serves two purposes. For one, doing this automatically creates a path with one contour and four nodes, exactly what is needed for a rectangular shape. Secondly, it also assures that no path properties are inherited from the document since paths created in this manner start with none regardless. Each of these rectangles is sized based on the pattern sizes (specified by patternHeight and endWidth) with the middle being sized based on text width. Whenever text is added or changed with UpdateText, that middle width is changed and needs to be updated. This is handled through the Resize function. Resize not only resizes the middle but repositions the right end to make sure it keeps up with the new size. In doing so it also makes sure to update the pattern added using ApplyPattern. ApplyPattern assigns as well as positions the pattern. Since the right end of the button is moving, it will need to make sure the pattern moves with it, hence the need for ApplyPattern to be called again for that path when Resize is called.

Note that no special attention paid to what font the button should use. As it stands now, the font used will simply be the default provided by Fireworks (the last font you used). If you want, you can edit the shape to be able to specify that.

 

Auto Shape Tools

Up to this point, all shapes created were designed for the Shapes panel in Fireworks. In practice, though, if you tend to use a lot of Fireworks shapes, you're probably using them from the Tools panel with the shapes listed under the Rectangle tool. These shapes include the Rounded Rectangle, the Doughnut, and the Star among others.

You too can create your own Auto Shapes to be used from this list. They work much in the same way as those created for the Shapes panel aside from one major difference: creation. These shapes are created not via drag and drop but from a click and drag.

When Auto Shape tools are created, three new events are utilized, BeginDragInsert, DragInsert, and EndDragInsert. These new events help distinguish events for the shape tool as it is inserted into a Fireworks document through a click and drag. The initial click represents the BeginDragInsert event. While the mouse is being dragged (and the shape being drawn) you get the DragInsert event followed by the inevitable EndDragInsert as a result of the user releasing from the drag. If, by chance, the user does not drag at all and just clicks and releases, the InsertSmartShapeAt event is called and the shape would be created much in the same way it would as if it were created from the Shapes panel. In fact, because Auto Shape tools use this event as well, they can be placed in the Shapes panel and work perfectly fine. For that matter, shapes from the Shapes panel, too, can just as well be Auto Shape tools... to an extent that is. They just won't be able to take advantage of the options available for shape insertion and may only work when you do not drag the mouse to make them. Many of Macromedia's shapes in the Shapes panel include a BeginDragInsert event handler in their code that just mimic InsertSmartShapeAt so that they could easily be used as tools if desired. When your treat a BeginDragInsert event like a InsertSmartShapeAt event, the result is a shape that, when the user drags, simply scales up from the point at which the user clicked. Many times this may be all that is needed from the shape tool (which is the case for many of the default Auto Shape tools), though this really doesn't make use of the advantages available to this new manner of shape creation. Nevertheless, it can be a useful alternative to a basic drag and drop insertion provided by shapes in the Shapes panel.

Auto Shape tools reside in the Configuration\Auto Shape Tools folder located in your Fireworks install directory. Like shapes added to the Shapes panel, tool shapes also have a preview image, though its size is slightly smaller at 16x16. This, too, can be either a PNG or GIF (PNG with alpha is typically preferred for Auto Shape tools). You can use the following style to help you maintain the same consistent look and feel for your Auto Shape tool icons so that they can look like those provided for Fireworks by default.

Auto Shape Tool.stl

If you haven't already, you may want to create a new JSF file in the tools directory to work from or even copy a file from the Auto Shapes folder to edit and have as an Auto Shape tool. In fact, lets take a shape we've already created, say, the Asterisk shape, and make it tool. In doing this I'd suggest copying the preview graphic (if you've made one for it) and quickly scaling it down to 16x16 so that it fits the tools menu. Otherwise the tools panel may try to use the larger version and cause some issues in the GUI.

Auto Shape Tool: Asterisk 3 [ Show/Hide Code | Download ]

The only change made was including the operation.BeginDragInsert function definition which is assigned to be exactly the same function as operation.InsertSmartShapeAt. This lets the shape create itself when inserted with a drag from the tools menu. Without any other interaction specified, it will react as previously described, simply scaling upward from the point the mouse was clicked.

 

The big advantage of Auto Shape tools is the "line-to" method of insertion. The above example of the Asterisk translates the line dragging into scaling. Another way to handle this is by making a shape that acts more like the line tool in Fireworks. Using the Auto Shape tool events, this is not hard to accomplish.

Such a tool would be a simple arrow tool. This would be an arrow which is drawn much like a line with the line tool but instead of a line, you, obviously, get an arrow. Draw a line from the click point to the mouse and throw an arrow on the end and you got your shape. Take a look.

Auto Shape Tool: Simple Arrow [ Show/Hide Code | Download ]

One thing to notice here is that no register move functions are used for the BeginDragInsert here yet smartShape.getsDragEvents is still needed to be set to true in order for DragInsert to be called. Without this, the default scale behavior takes over, DragInsert is never handled, and the shape basically breaks. The DragInsert function here simply calls the Draw function which makes the arrow based on the position of the mouse and the location of the first node of the first contour which was set to be the position of the mouse click in BeginDragInsert. The control points aren't created until EndDragInsert since they are useless until then (and may get in the way when trying to create precise arrows in your Fireworks document). A basic RegisterMove is used with each of those though smartShape.getsDragEvents is assigned to be true again to make sure the shape can call Draw itself for each DragControlPoint event.

You may also, there is no InsertSmartShapeAt event handler defined for the Simple Arrow shape. This is a bad idea since clicking without dragging will create an empty shape. Every tool should handle a InsertSmartShapeAt event as well as the others for shape creation. I guess I'm just lazy; or maybe I just expect people to use the tool right? You decide.

 

Aside from the new events, Auto Shape tools also gain access to the fifth register move function, RegisterInsertBBoxMove. This is for use in the BeginDragInset event and allows special control over Auto Shape sizing as it is being dragged onto the screen. It is much like RegisterMove and it supports deltaXtoX and the related for a RegisterMoveParms object, but also allows for other delta properties which are based on the sizes of sides of the bounding box being dragged for a new Auto Shape tool, deltaShortestSideToX, deltaShortestSideToY, deltaLongestSideToX, and deltaLongestSideToY. These properties are like other delta properties but let you base movement of points on either the shortest or longest side of the dragged bounding box. This will let you essentially let points react as though the bounding area drawn is square letting you more conveniently manage proportions for thicknesses.

We can again revisit another past shape as a demonstration. This time we'll step as far back as the Hollow Box shape. Imagine dragging the Box shape onto a Fireworks document as a tool. Consider what would happen the the thicknesses of the sides when you drag the box out to be very wide. The sides on the left and right will be much wider than those on the top and bottom since side thickness is proportional to the shape's width and height. Width size controls the left and right sides thick while height size controls the top and bottom. Since these work independently of each other, you can easily have sides disproportional to each other.

You can see this in action using the slightly modified Box shape code (modified to include BeginDragInsert).

Auto Shape Tool: Hollow Box 5 [ Show/Hide Code | Download ]

To fix this, you'd use RegisterInsertBBoxMove. RegisterInsertBBoxMove can be used to instruct the shape too look for the smallest size (width or height) by using deltaShortestSideToX and deltaShortestSideToY and base point positioning on that.

All that is needed is the appropriate application of RegisterMoveParms properties and RegisterInsertBBoxMove. You can see just what those applications may be in the following.

Auto Shape Tool: Hollow Box 6 [ Show/Hide Code | Download ]

The outer contour's register move function's are set first. These stick to using deltaXtoX and deltaYtoY to have the points follow either the x and/or y positions of the mouse. There's nothing special there because it's the inner contour that decides the proportion of thickness for the box. As you can see, deltaXtoX and deltaYtoY relations remain the same for respective points on this contour but, in addition, you have extensive use of the additional properties of deltaShortestSideToX and deltaShortestSideToY. What they do is adjust the the contours nodes to move an additional amount based on the size of the smallest side of the dragging bounding box. This makes it so those points are mimicking the outer contour but each moving inward towards the center of the box at a like distance, i.e. 1/5 the size of the shortest side (1/5 is proportional to the dimensions obtained with InsertSmartShapeAt).

We can continue the scaling after the tool has been made with some control points. The current control point selection for the Box is a little light; it could definitely use some more. We'll include 2 more, one for scaling the box as a whole (by the lower right corner), and the other for scaling the inner box as to allow adjustment of the border width (in lower right corner of border contour).

"Box" elements like this shape scale best with normal RegisterMove movement. If you haven't already noticed, Auto Shape tools automatically support uniform scaling by using SHIFT during tool creation. This can also be simulated with RegisterMove and the constraint properties of a RegisterMoveParms object. Here, the box can use the same constraint as that provided by the tool by default, a constraint of 45 degrees. The constrain45Key property will do this for us by allowing such constraint to be associated with a keyboard key, one like SHIFT. With this property set, pressing the key associated with it will cause registered points to move only in 45 degree angles.

This is desired since this Box shape is all about uniform. It's always nice to have the option for such control.

Both new control points will be able to move with a 45 degree constraint. However, it is important to outline just how they will move when they are moved. There are many different kinds of scaling. How should this shape be scaled? Given that, as previously mentioned, this shape is about uniformity so the scaling should reflect that; not just in a 45 degree constraint, but also in maintaining correct proportions with the border. This is actually more of a concern for the control point scaling the whole shape. The other control point for the inner border width is a little more straightforward.

The inner control point for the Box shape provides a way to resize the border internally. For this, movement for that control point will need to be restricted to the interior of the the shape. This is easily accomplished using the min and max properties for a RegisterMoveParms object. The only other requirement for this control point is the use of constrain45Key. No sweat.

Then we get to the outer control point which will resize the whole shape and, lest we forget, maintain proportions within the border. Now, there are a couple of ways to tackle this. I'm going to opt for a Draw function approach since I don't want to have to deal with the assignment of register move functions for all the control points and nodes (and there really aren't that many of them either). Given the locations of the control points, the entire shape can be created from their whereabouts alone. This means no node needs to ever be assigned a register move function and their can be set solely by the relationships of the control points on the screen. Now, when the outer control point is moved, border uniformity can be maintained simply by having the inner control point move along with it, the shape itself using their relationship with each other to redraw the entire shape. Since their relationship won't change, the border size won't change, just the overall size of the shape itself. Like the inner control point, though, the outer control point, too, would need some min/max action going on to make sure the border doesn't collapse on itself.

One thing not to be forgotten is that central control point. This has no real actions associated with it, but is used slightly in size calculation so it needs to be addressed in a shape resize. Basically, it needs to be in the center of the shape. This can be easily accomplished in the Draw function by placing the control point between the upper left and lower right points of the shape. To do that, we can use he following new point function.

PointBetween = function(pt1, pt2){
	return {x:(pt1.x + pt2.x)/2, y:(pt1.y + pt2.y)/2};
}

There's one more thing I'm going to do with this shape. Just to prevent a little unnecessary assignment, I'm going to take operation.BeginDragInsert and separate it from operation.InsertSmartShapeAt so that RegisterInsertBBoxMove will only be called for points in the BeginDragInsert event. This is by no means required, but it does make me feel a little better that all that RegisterInsertBBoxMove craziness isn't being called in vain for InsertSmartShapeAt events.

Auto Shape Tool: Hollow Box 7 [ Show/Hide Code | Download ]

And there you have it, a Box shape tool that maintains border uniformity when created and scaled (though both in different ways).

Continue reading »

Pages: 1 | 2 | 3 | 4 | 5