Learning English Online, Free English Lessons For Everyone
_Sponsors_
___ ______

Flash CS3: Lesson 9: Drag-and-drop with An improved geometry application PDF  | Print |  E-mail
Wednesday, 13 April 2011 17:06

Flash CS3: Lesson 9: Drag-and-drop with An improved geometry application

To put all of our skills together, we end with a mathematical exploration of Pick's Theorem, which has to do with the relationship between the area of a triangle and the number of lattice points that it touches. the hypothesis for this theorem is that the triangle is drawn with all three vertices on lattice points. We can easily accomplish this by combining the fancy triangle drawing example shown on Page 3 with the "snap to grid" idea from Page 4. In addition, we can use the hitTestPoint idea from Page 5 to count the number of lattice points in the interior of our triangle. Here is the finished applet for you to play with before we delve into the code:

 

We set up the "board" in exactly the same way we did on Page 4, with the only difference being that we need three separate draggable points instead of just one.

//Part I -- Set up the board

 

var bWidth:Number = 400;

var bHeight:Number = 300;

var grid:Number = 50; // Size of the grid and number of lattice points in each direction

var dotsWide:Number = Math.ceil(bWidth/grid) - 1;

var dotsHigh:Number = Math.ceil(bHeight/grid) - 1;

 

var board:Sprite = new Sprite();

var triangle:Sprite = new Sprite();

var dragPt1:Sprite = new Sprite();

var dragPt2:Sprite = new Sprite();

var dragPt3:Sprite = new Sprite();

 

this.addChild(board);

board.addChild(triangle);

board.addChild(dragPt1);

board.addChild(dragPt2);

board.addChild(dragPt3);

 

board.graphics.lineStyle(1,0);

board.graphics.beginFill(0xCCCCCC);

board.graphics.drawRect(0,0,bWidth,bHeight);

board.graphics.endFill();

 

// Add a bunch of small circles to represent lattice points

board.graphics.beginFill(0x000000);

for (var i=1; i<=dotsHigh; i++) {

for (var j=1; j<=dotsWide; j++) {

board.graphics.drawCircle(j*grid,i*grid,2);

}

}

board.graphics.endFill();

board.x = 10;

board.y = 10;

dragPt1.graphics.lineStyle(1,0);

dragPt1.graphics.beginFill(0x00FF00,0.9);

dragPt1.graphics.drawCircle(0,0,5);

dragPt1.graphics.endFill();

dragPt1.x = goodX(50);

dragPt1.y = goodY(50);

 

dragPt2.graphics.lineStyle(1,0);

dragPt2.graphics.beginFill(0x00FF00,0.9);

dragPt2.graphics.drawCircle(0,0,5);

dragPt2.graphics.endFill();

dragPt2.x = goodX(50);

dragPt2.y = goodY(250);

 

dragPt3.graphics.lineStyle(1,0);

dragPt3.graphics.beginFill(0x00FF00,0.9);

dragPt3.graphics.drawCircle(0,0,5);

dragPt3.graphics.endFill();

dragPt3.x = goodY(150);

dragPt3.y = goodY(150);

 

The drag-and-drop functionality is handled the same way it was on Page 3, where we use the global variable "thisPt" to point to the object that gets the MOUSE_DOWN event (using the events currentTarget property) and then we move "thisPt" around based on the MOUSE_MOVE event. In addition to the helper function that redraws the triangle when the mouse is dragged, we also have a helper function that computes the values (i.e., triangle area and the number of interior lattice points and boundary lattice points) and updates the text boxes on the stage that display those values.

 

// Part II -- Add drag and drop functionality

 

var thisPt:Sprite = new Sprite();

 

dragPt1.addEventListener(MouseEvent.MOUSE_DOWN, startMove);

dragPt2.addEventListener(MouseEvent.MOUSE_DOWN, startMove);

dragPt3.addEventListener(MouseEvent.MOUSE_DOWN, startMove);

 

function startMove(evt:MouseEvent):void {

thisPt = Sprite(evt.currentTarget);

stage.addEventListener(MouseEvent.MOUSE_MOVE, pointMove);

}

 

function pointMove(e:MouseEvent):void {

thisPt.x = goodX(board.mouseX);

thisPt.y = goodY(board.mouseY);

drawTriangle();

countPoints();

e.updateAfterEvent();

}

stage.addEventListener(MouseEvent.MOUSE_UP, stopAll);

 

function stopAll(e:MouseEvent):void {

stage.removeEventListener(MouseEvent.MOUSE_MOVE, pointMove);

}

 

The helper functions called drawTriangle, goodX and goodY are a straightforward combination of those that appear on Pages 3 and 4 of this tutorial. The new helper function called countPoints illustrates a couple of new ideas worth talking about.

  1. We use the hitTestPoint method of the triangle sprite to count the number of lattice points that hit the triangle. Unfortunately, this method requires coordinates in absolute stage coordinates (instead of coordinates relative to the parent object, the board in this case); hence, we have to do a translation by adding This process gives us the total number of points on interior and boundary, so we have to do more to split this count into the two components.
  2. We use some math to count the number of lattice points on the boundary since the hitTestPoint method is not reliable when the graphic is a mere line as it is for the boundary segments of our triangle. It is easy to use the following fact: If a line segment between lattice points has a y-change (a.k.a., "rise") of S and an x-change (a.k.a., "run") of N, then the number of lattice points on the line segment is 1 + gcd(S,N).
  3. The gcd helper function is a typical recursive implementation. Note that we add some robustness to keep the function from being misused if someone places it in a program where negative input values could be used. This is not a concern in this particular application.
  4. If the area of the triangle is 0, then Pick's Theorem does not apply (since it would be wrong), so we have to provide a reasonable message to the user if this occurs.

/*

Part III -- Helper functions to count the number of lattice points within the triangle and on the boundary, to update the triangle graphic, and to stay within boundary / snap to grid. Note that the countPoints function needs its own helper function to compute the gcd (greatest common divisor) of two integers.

*/

function countPoints():void {

var c_total:int = 0;

var c_border:int = 0;

var i,j,ax,ay,bx,by,cx,cy:int;

var t_area:Number;

 

for (i=1; i<=dotsHigh; i++) {

for (j=1; j<=dotsWide; j++) {

if (triangle.hitTestPoint(board.x + j*grid, board.y + i*grid, true)) {

c_total++ ;

}

}

}

 

/*

Compute the number of lattice points on the boundary using gcd's of "rise" and "run" for each triangle edge.

*/

ax = int(Math.abs(dragPt1.x - dragPt2.x)/grid);

ay = int(Math.abs(dragPt1.y - dragPt2.y)/grid);

bx = int(Math.abs(dragPt1.x - dragPt3.x)/grid);

by = int(Math.abs(dragPt1.y - dragPt3.y)/grid);

cx = int(Math.abs(dragPt3.x - dragPt2.x)/grid);

cy = int(Math.abs(dragPt3.y - dragPt2.y)/grid);

 

c_border = gcd(ax,ay) + gcd(bx,by) + gcd(cx,cy);

t_area = c_total - c_border/2 - 1;

 

if (t_area == 0 ) {

txtInterior.text = "Degenerate";

txtBoundary.text = "Degenerate";

txtArea.text = "Degenerate";

}

else {

txtInterior.text = String(c_total - c_border);

txtBoundary.text = String(c_border);

txtArea.text = String(t_area);

}

}

 

function gcd(a:int, b:int):int {

if (a<0) {

return gcd(-a,b);

}

if (b<0) {

return gcd(a,-b);

}

if (a==0) {

return b;

}

if (b==0) {

return a;

}

if (a < b) {

return gcd(a, b%a);

}

else {

return gcd(b, a%b);

}

}

 

function drawTriangle():void {

triangle.graphics.clear();

triangle.graphics.lineStyle(2,0xAAAAFF);

triangle.graphics.beginFill(0xAAAAFF,0.75);

triangle.graphics.moveTo(dragPt1.x,dragPt1.y);

triangle.graphics.lineTo(dragPt2.x,dragPt2.y);

triangle.graphics.lineTo(dragPt3.x,dragPt3.y);

triangle.graphics.lineTo(dragPt1.x,dragPt1.y);

triangle.graphics.endFill();

}

 

function goodX(inX:Number):Number {

if (inX > grid*dotsWide) {

return grid*dotsWide;

}

 

if (inX < grid) {

return grid;

}

 

return grid*Math.round(inX/grid);

}

 

function goodY(inY:Number):Number {

if (inY > grid*dotsHigh) {

return grid*dotsHigh;

}

 

if (inY < grid) {

return grid;

}

 

return grid*Math.round(inY/grid);

}

 

Finally we add a couple of lines so that the triangle and count appears even before any dragging has taken place.

// Initial drawing and count

drawTriangle();

countPoints();

Download

__Please, Click here to have another Lesson about Drag-and-Drop in Flash CS3____

To illustrate drag-and-drop in ActionScript 3, we will first make some simple applications that involve dragging a circle on a rectangular board.

Each of the following examples have the following common code to draw a rectangular board on the stage and then add a circle as a child of the board. It might seem counterintuitive to build the relationships between myPoint, board, and stage before the graphics are added, but doing all of the "addChild" calls up front assures that the layering of the objects is exactly what we want. (The relationship between the board and the circle, though natural, can cause some issues with mouse events as we will see in a later tutorial.) Both of our objects are Sprites, with the "board" visually represented by a gray rectangle and the "myPoint" represented by a blue circle that is fairly large to illustrate some issues.

 

// Part 1 -- Setting up the objects

 

var board:Sprite = new Sprite();

var myPoint:Sprite = new Sprite();

 

this.addChild(board);

board.addChild(myPoint);

 

board.graphics.lineStyle(1,0);

board.graphics.beginFill(0xCCCCCC);

board.graphics.drawRect(0,0,400,300);

board.graphics.endFill();

board.x = 10;

board.y = 10;

 

myPoint.graphics.lineStyle(1,0);

myPoint.graphics.beginFill(0x0000FF,0.7);

myPoint.graphics.drawCircle(0,0,20);

myPoint.graphics.endFill();

myPoint.x = 50;

myPoint.y = 50;

 

Using the startDrag and stopDrag methods

Display Objects have methods called startDrag and stopDrag that make it easy to add this type of interface quickly to any application. The following additional code will add this functionality. The basic idea is that when the mouse is pressed down over our circle (the sprite named "myPoint"), the startDrag method is called and when the mouse is let up over our circle, the stopDrag method is called. Both startDrag and stopDrag are built-in methods of the Sprite class, so nothing else is needed.

 

// Part 2 -- Add drag-and-drop functionality

 

myPoint.addEventListener(MouseEvent.MOUSE_DOWN, startMove);

 

function startMove(evt:MouseEvent):void {

myPoint.startDrag();

}

 

myPoint.addEventListener(MouseEvent.MOUSE_UP, stopMove);

 

function stopMove(e:MouseEvent):void {

myPoint.stopDrag();

}

 

Download

Notes

  • This is quick because it requires so little code.
  • The dragging looks a little rough.
  • The dragging goes beyond the boundaries of the box. This can be remedied by adding an argument to the startDrag method call that specifies a rectangular boundary that the object cannot be dragged beyond. To use this, you should look up the help file for startDrag and be prepared to follow up by reading up on the Rectangle class.
  • It is not clear from this simple example, but we cannot easily control other actions that should be completed during the dragging of the ball. For example, if we want a line to be traced having the ball as one endpoint, it is not easy to have this line updated continuously as the ball is dragged, as one would want.

Drag-and-drop with the MOUSE_MOVE event

Certainly we could make the most of the built-in methods, but because we have greater ambitions for our drag-and-drop interface, we might as well start thinking now about how to do it ourselves. To fashion our own drag-and-drop interface that will give us more control, we examine the MOUSE_MOVE event that is triggered whenever the mouse is moved. We will deliberately take a few attempts at this so that we can better understand some of the subtleties of mouse events.

In our first attempt, we use the following code in place of the "Part 2" code shown above. The difference here is that when the "myPoint" sprite hears the MOUSE_DOWN event, a new listener is attached to myPoint so that it will subsequently hear the MOUSE_MOVE event. When the MOUSE_MOVE event is heard by myPoint, the x and y coordinates of myPoint are changed to correspond to the x and y coordinates of the mouse. Note that we use board.mouseX and board.mouseY because the values myPoint.x and myPoint.y refer to the coordinates of myPoint relative to its parent object, which is the "board" sprite. Hence, these need to be set to the x and y coordinates of the mouse relative to "board." When the MOUSE_UP event is heard, we simply remove the listener registered with the MOUSE_MOVE event, effectively stopping the motion. Also notice the use of the method updateAfterEvent, which refreshes the screen immediately after the mouse move event, making the "myPoint" sprite stay with the mouse cursor as much as possible.

 

// Part 2 -- First attempts are never good

 

myPoint.addEventListener(MouseEvent.MOUSE_DOWN, startMove);

function startMove(evt:MouseEvent):void {

myPoint.addEventListener(MouseEvent.MOUSE_MOVE, pointMove);

}

function pointMove(e:MouseEvent):void {

myPoint.x = board.mouseX;

myPoint.y = board.mouseY;

e.updateAfterEvent();

}

myPoint.addEventListener(MouseEvent.MOUSE_UP, stopMove);

function stopMove(e:MouseEvent):void {

myPoint.removeEventListener(MouseEvent.MOUSE_MOVE, pointMove);

}

 

Nothing in the design addresses the problems with the startDrag/stopDrag approach that we listed in the previous section. In fact, we have made things worse. Try out the applet and see if you can spot its other deficiencies.

The motion is smoother unless you move your mouse too fast in which case the motion actually stops! This happens because we registered our listener for the MOUSE_MOVE event with the circle (the sprite called "myPoint"), and so the MOUSE_MOVE event is only triggered when the mouse moves over the myPoint sprite. In reality, even though only the position of the myPoint sprite is affected by the mouse move, we want the mouse move event to trigger when the mouse moves anywhere over the stage. In addition, we register the listener for the MOUSE_UP event with the stage so that the motion will be stopped when the mouse button is released regardless of where the mouse is located.

The following code (which is identical to the above except that "myPoint" is changed to "stage" in three places, highlighted in bold text) accomplishes the change and gives a very nice smooth motion, as the applet the follows attests.

 

// Part 2 -- Better Attempt

 

myPoint.addEventListener(MouseEvent.MOUSE_DOWN, startMove);

 

function startMove(evt:MouseEvent):void {

stage.addEventListener(MouseEvent.MOUSE_MOVE, pointMove);

}

 

function pointMove(e:MouseEvent):void {

myPoint.x = board.mouseX;

myPoint.y = board.mouseY;

e.updateAfterEvent();

}

 

stage.addEventListener(MouseEvent.MOUSE_UP, stopMove);

 

function stopMove(e:MouseEvent):void {

stage.removeEventListener(MouseEvent.MOUSE_MOVE, pointMove);

}

 

If you think that you only want the "mouse move" event to trigger when you move the mouse over the sprite "board", make this change yourself and give this version a try as well. It works fine, but the behavior when the mouse is dragged off of the board is not as satisfactory as our solution below.

Download

  • Flash CS3 file DragDrop1.fla contains the improved version only.

Notes

  • This gives very smooth motion and functions that handle the repositioning of the point can be extended to manage all other manners of updates or changes while the mouse is in motion.
  • The dragging still goes beyond the boundaries of the box, and indeed the visible part of the stage!
  • You can drop the object off of the stage never to be seen again!

"Mouse move" drag-and-drop with restricted position

Being able to drop an object off-screen is clearly undesirable, so before we build a sample application we need to fix this problem. There are several possible solutions, but the one we will present has the advantage of being flexible enough for further improvements as needed.

We will add the following code to the end of the previous application to define functions called "goodX" and "goodY" that translate the current x and y mouse coordinates into "useable" x and y coordinates. In this case, we simply give instructions that if the current coordinate is too big or too small, then we should really use the biggest (resp. smallest) allowable value instead. To keep the point on the board, it is clear that the smallest allowable x value is simply 0, but the largest allowable x value is the width of the "board" graphic object, which is 400 by our Part I code. We have a similar issue with the y-coordinate.

 

// Part III

 

function goodX(inX:Number):Number {

if (inX < 0) {

return 0;

}

 

if (inX > 400) {

return 400;

}

 

return inX;

}

 

function goodY(inY:Number):Number {

if (inY < 0) {

return 0;

}

 

if (inY > 300) {

return 300;

}

 

return inY;

}

 

Adding this code alone will have no effect -- we need to place the myPoint sprite based on the transformed coordinates, hence we need the following revision of the pointMove function within Part II of our code. Only the new function definition with the two highlighted changes from the previous version is shown.

 

// Revised definition of pointMove in Part II of our script

 

function pointMove(e:MouseEvent):void {

myPoint.x = goodX(board.mouseX);

myPoint.y = goodY(board.mouseY);

e.updateAfterEvent();

}

 

Try this version out below. We think you will agree that the user interface is very nice, and we will see in our first easy application that the do-it-yourself approach allows for a great deal of flexibility.

Download

Notes

  • This gives very smooth motion and functions that handle the repositioning of the point can be extended to manage all other manners of updates or changes while the mouse is in motion. It stops at a sensibly chosen boundary and properly handles the motion of the mouse outside of this boundary.
  • We are playing with fire by having the constants 400 and 300 appear twice in our code. It would be easy for a programmer to inadvertently change these in only one place, leading to bad behavior that would be very difficult to debug. We really should declare variables bWidth = 400 and bHeight = 300 at the top of our script and then use the variable names where these values are needed. We adopt this common sense practice to all subsequent files in this tutorial.
  ________


_____
We have 3 guests online

If you don't see anything in the middle of this page, you may need the latest Flash plug-in.
It only takes a few moments to
Download it FREE at Adobe.com. get adobe Flash player

Teaching English For Kids In Primary School

Stydying English Online For Everyone

Game for Kids


Home || Misterduncan|| Flash Cards || Songs For Kids|| Free Games || Learn English with GOGO|| Teaching English For Childern|| ESL/EFL Kids Course ||
Designed by Learning English Online. Designed by: Free Joomla Theme, mysql. Valid XHTML and CSS.