- Initializing the Paint Program
- Creating the Sliding Color Chooser
- Scripting the Tool Buttons
- Scripting the Paint Tools
- How It Works
Scripting the Paint Tools
Now that all the setup work is done, it's time to build the script that will actually make the custom pointer follow the mouse, color the picture elements, and draw the lines and shapes. These tasks are best performed with the help of Clip Event handlers, and because any Movie Clip will do, the script will be assigned to the Current Color Display (the larger white circle to the lower-right; see Figure 20).
As in the previous set of Clip Events, you will work with mouseDown first, setting it to capture all mouse clicks. In SpacePainter, several things might happen on a mouse click, depending on what the current tool is and the position of the cursor. So, each Clip Event handler consists of a number of if statements that check the current state of things and react appropriately. In this first few lines of code, an if statement checks to see if the current tool is the Paint Tool.
Figure 20 In this section, you apply the scripts to the ColorDisc movie clip (which has an instance name of CurrentColorDisplay).
-
Select the Current Color Display instance, open the Actions panel, and assign this code (see Figure 21):
onClipEvent ( mouseDown ) { if (_root.ToolType == "Paint" && _root.MouseInBounds()) { eval("_root." && substring[ccc] (_root.Pointer._droptarget, 2, 50) && "Color").setRGB(_root.CurrentColor.getRGB());
Figure 21 The onClipEvent(MouseDown) code is assigned to the CurrentColorDisplay instance.
If ToolType does equal Paint and the returned value of the MouseInBounds() function is true, eval and the setRGB method are used to alter the color of whatever object is being clicked. The address of the object being clicked is determined using the _droptarget property of Pointer.
The next tool to be accommodated is the Eraser. The Eraser behaves differently depending on whether it is applied to a drawn shape or a picture element. If the user clicks a picture element with the Eraser, the element turns white. If the user clicks a shape, the shape is removed using the removeMovieClip action.
Insert the Eraser tool code:
} else if (_root.ToolType == "Eraser" && _root.MouseInBounds()) { if (substring(_root.Pointer._droptarget, 2, 5) == "Shape") { eval("_root." && substring(_root.Pointer._[ccc] droptarget, 2, 50)).removeMovieClip(); } else { eval("_root." && substring(_root.Pointer._[ccc] droptarget, 2, 50) && "Color").setRGB(0xFFFFFF); }
It's known that picture elements have instance names such as Element1 and Element2, and drawn shapes are named Shape1 and Shape2. So, if the first five letters of the clicked object's name spell "Shape," the script knows how to react.
-
Add this code (see Figure 22):
} else if (_root.ToolType == "Pen" && _root.MouseInBounds()) { PenDown = true;
Figure 22 The code is added to the CurrentColorDisplay() movie clip instance.
The Paint and Eraser tools are "click" tools because you click the object you want to change and that's it. The Pen and the shape tools are "click and drag" tools, whereby you click and then move the mouse around. So, when the mouse is clicked with the Pen tool active, the PenDown variable is set to true to indicate that the dragging action has now begun. The remainder of the Pen script will be added in the enterFrame handler.
So far, the Paint, Eraser, and Pen tools have been accommodated in this mouseDown handler. The last ones to add are the shape tools.
To complete the mouseDown handler, insert this code:
} else if ((_root.ToolType == "Line" || _[ccc] root.ToolType == "Ellipse" || _root.ToolType == [ccc] "Rectangle") && _root.MouseInBounds()) { PenDown = true; ShapeCount ++; X1 = _root._xmouse; Y1 = _root._ymouse; _root.attachMovie(_root.ToolType,"Shape" + [ccc] ShapeCount, ShapeCount + 100); eval("_root.Shape" + ShapeCount + "Color") = [ccc] new Color("_root.Shape" + ShapeCount); eval("_root.Shape" + ShapeCount + "Color")[ccc] .setRGB(_root.CurrentColor.getRGB()); } }
This block of code creates a new instance of the currently selected shape tool, positions it at the same coordinates as the mouse cursor, and then changes its color to reflect the currently selected shade.
Now that all the mouseDown possibilities have been taken care of, the next handler to add is mouseUp. This code is executed whenever the user releases the mouse button.
Insert the following code:
onClipEvent ( mouseUp ) { PenDown = false; }
This event handler is rather short because its only job is to set PenDown to false, indicating that the user is no longer dragging. The handler doesn't need to check which tool is active because PenDown would be false anyway (because the user released the button).
-
Moving right along, the next Clip Event to be captured is enterFrame (see Figure 23). This code is executed many times every second, so it is the appropriate place to put instructions for resizing shapes and drawing pen linesall the things that need to appear as though they're reacting fairly snappily to the user's mouse movement. Input this code to begin the enterFrame handler:
onClipEvent ( enterFrame ) { FrameCount ++; if (FrameCount % 2 != 0 && _root.ToolType == "Pen") { X1 = _root._xmouse; Y1 = _root._ymouse; } else { X2 = _root._xmouse; Y2 = _root._ymouse; }
Figure 23 The onClipEvent(enterFrame) code is added to the script assigned to the CurrentColorDisplay() movie clip instance.
The aim of this first block of code is to record two pairs of coordinate points, one on odd frames and the other on even frames. The Pen tool (see Figure 24) uses these two coordinate pairs to follow the movement of the mouse. Of particular interest here is the use of the Modulo (%) operator to determine whether a frame is odd or even. FrameCount is incremented after every frame draw, so if FrameCount % 2 equals 0, the value of FrameCount is divisible by two without any remaindermaking that value an even number such as 2, 4, or 6. If FrameCount % 2 does not equal 0 (denoted by the code != 0), the value of FrameCount is an odd number.
Notice that only the first pair of coordinates requires that the ToolType be Pen. This is because the other shape tools are anchored once at a particular point, but then the second point changes as the mouse is dragged. When the Pen is active however, both points change constantly.
Figure 24 The Pen tool works by duplicating copies of the Pen Line symbol between two points on a continual basis (or at least until the visitor releases the mouse button).
Drawing the no-math way! The Pen tool script could be quite an involved process of calculating angles with trigonometry, but, thanks to a clever technique, it's actually quite simple. The enterFrame handler constantly tracks mouse movement by maintaining two pairs of X and Y coordinates, each one updated on alternate keyframes. So, if the cursor is moving, there will always be a gap between points X1, Y1 and X2, Y2. attachMovie is then used to create a new copy of the Pen Line symbol, and it is squeezed into that gap, creating a continuous line that traces all the points covered by the user's mouse motion. The same Pen Line symbol is copied multiple times and then stretched to fit between the points the user has traced with the mouse. No math is required other than to calculate the _xscale, _yscale and position of each Pen Line instance.
Complete the enterFrame handler script by adding this code:
if (PenDown && _root.MouseInBounds()) { if (_root.ToolType == "Pen") { ShapeCount ++ _root.attachMovie(_root.ToolType,"Shape" + [ccc] ShapeCount, ShapeCount + 100); } with (eval("_root.Shape" + ShapeCount)) { _x = ((X2 - X1) / 2) + X1; _y = ((Y2 - Y1) / 2) + Y1; _xscale = X1 - X2; _yscale = Y1 - Y2; } } }
This block of code serves a dual purpose. The first part is specific to the Pen tool and is responsible for creating multiple copies of the Pen Line symbol. The second part works for all the drawing tools, resizing the current shape depending on the values of X1, Y1 and X2, Y2. The effect is to squeeze the shape so that it fits between its anchor point and the current mouse position.
The code for making the custom mouse cursor move (the mouseMove event) is used in conjunction with the startDrag action to force the Pointer symbol to "stick" to the mouse cursor. The updateAfterEvent() action is added to allow the cursor to be updated independently of the movie's frame rate. This eliminates a lot of the sluggishness usually associated with the startDrag action.
Insert the following code:
onClipEvent (mouseMove) { startDrag("_root.Pointer", true); updateAfterEvent(); }
The SpacePainter application is now complete!
Use Test Movie to export the file.