Click here to Skip to main content
15,039,214 members
Articles / Web Development / HTML5
Article
Posted 27 Apr 2016

Stats

10K views
202 downloads
7 bookmarked

Algorithm : Calc Convex Hull & Draw HTML5 Canvas (Part 3 of N)

Rate me:
Please Sign up or sign in to vote.
4.75/5 (3 votes)
27 Apr 2016CPOL9 min read
We learn how to generate random points (easy) and we enable functionality which allows the user to grab any point on the grid and move it around in real-time (see animated gif).

Introduction

After we complete this article the final code in TrapPoints_v008.zip will allow you to move points and see them redrawn in real-time on the canvas.

move points in real time

This article continues the work that we've been doing in the two previous articles published here at CP:

Algorithm : Calc Convex Hull & Draw HTML5 Canvas (Part 1 of 2)[^]

Algorithm : Calc Convex Hull & Draw HTML5 Canvas (Part 2 of N)[^]

What This Article Will Cover

  1. adding a button and functionality to create randomly generated points
  2. make it so you can move points -- seems like an interesting challenge

Note: Okay, I'm getting to the convex hull algorithm.  But, all this drawing stuff got to me.  :)

See Live Version

Try the live version which includes these updates at:
  http://raddev.us/TrapPoints[^]

Background

All of the work done in these articles is inspired by my reading the book :  Amazon.com: Algorithms in a Nutshell: A Practical Guide (9781491948927): George T. Heineman, Gary Pollice, Stanley Selkow: Books[^]

It's a very interesting book and I've only made it as far as this first algorithm but it's quite readable.

HTML5 Canvas

I wanted a good way to examine how to go about solving this algorithm and HTML5 Canvas provided me the most ubiquitous graphics drawing functionality that is well-documented and 

Step 7 Begins Here

Add Auto-Generation of Points

You can see the completed code by downloading TrapPoints_v007.zip at the top of this article.

This is a very easy task.  First of all we just add a new Bootstrap-styled button so we can run the functionality.

The HTML will look like the following:

HTML
<button onclick="generateRandomPoints(50);" class="btn btn-primary">Generate Points</button>

I set up the onclick to call a method named generateRandomPoints() which takes the number of points we want to generate (in this case 50).  

generateRandomPoints()

This method is very easy to implement.

JavaScript
function generateRandomPoints(pointCount){
    clearPoints(); // empty out both arrays
    for (var i = 0; i < pointCount;i++){
        var X = Math.floor(Math.random() * CANVAS_SIZE); // gen number 0 to 649
        var Y = Math.floor(Math.random() * CANVAS_SIZE); // gen number 0 to 649
        var p = {};
        p.x = X;
        p.y = Y;
        allPoints.push(p);
    }
    draw();
}

The first thing we do is call the clearPoints() method, which we wrote in the previous article.  That method clears our allPoints and selectedTriangle point arrays, since those points won't be valid any longer.

After that, we loop through some code for each point that we want to create.

As we loop through we simply generate a random value using the built-in JavaScript method Math.random().  Math.random() generates a double value Z where 1 > Z >= 0.  

I've added a new constant* to the top of trappoints.js which looks like : var CANVAS_SIZE = 650; 

Since this value represents the max height and width of the canvas I never want to exceed these values.

Generating X and Y Values For a Point

Also, notice that we implement another built-in JavaScript method (Math.floor()) when generating our values.

The Mozilla Developer Network docs describes that method as:

Quote:

The Math.floor() function returns the largest integer less than or equal to a given number.

This just insures the value is always an integer since it simply truncates any decimal value (no rounding of the integer value).

*JavaScript doesn't support the keyword const across all browsers so I'm just creating a var and calling it a const.

We generate two different values using this random method (one for the x value and one for the y).

We assign each of those values to a local variable, generate a local point object out of the two values and then push the new point on the allPoints array.   The loop runs and generates a point each time throuhg.

Finally, when all the points are added to allPoints, we call the draw() method one time, which draws them all on the canvas.

Step 8 Begins Here

You can see the completed code by downloading TrapPoints_v008.zip at the top of this article.

Allow User To Move a Point

Now, we want to allow the user to move a point.  This one intrigues me because I think the end result will look cool, but I don't believe it will be too difficult.

Use Ctrl Key

Since I used the Shift Key to allow user to highlight points for the selected triangle I am now going to use the Ctrl key to indicate that the user is moving a point.  

Adding the code is quite easy. All we have to do is go to the mouseDownHandler() function and add the following code to the if...else... statement:

JavaScript
else if (event.ctrlKey){
            console.log("control key is pressed...");
        }

If you add that code and then run again, you will see that if you hold the Ctrl key down and click anywhere in the grid then no point is drawn.  Instead, just a message is out put to the web browser's console window.

We'll fill in the code to do the work in a moment.

Redraw Point As User Drags It On Canvas

The really cool feature is for the user to see the point being dragged around on the canvas so I want the point that is being moved by the user to continually redraw it until he lets go of his mouse button.

To do that work we need to add a mouseMove handler.  Let's do that now.

Go back to the initApp() method which we wrote in the first article and add one line (bolded in the code below)

JavaScript
function initApp()
{
    theCanvas = document.getElementById("gamescreen");
    ctx = theCanvas.getContext("2d");
    
    ctx.canvas.height  = CANVAS_SIZE;
    ctx.canvas.width = ctx.canvas.height;

    window.addEventListener("mousedown", mouseDownHandler);
    window.addEventListener("mousemove", mouseMoveHandler);
    initBoard();
}

With that we've registered a new method (which we will write the implementation for) named mouseMoveHandler.

Summary of How Point Moving Should Work

Here's my idea of how all this will hopefully work.  If the user's Ctrl key is down and the user clicks a spot on the Canvas, then we'll hit test the point where the user has clicked.  That's basically the same code we ran when the user is highlighting points for the selected triangle (see more in the 2nd article).

If a point is hit then we'll do something a little different this time.  Previously we just returned a point with the same coordinates and then we drew a highlighted (red) circle over the original one.  That worked well enough.

Moving One Point Means Redrawing Grid and All Other Points

However, now we want to draw over the one that we are moving (to erase it) and then redraw it every time it is moved.  That means we actually have to redraw the background and all the other points also.  We know that the draw() method currently does that so that will help.

We only want to do this when the user has captured a point (Ctrl key is down and hit test returns a point).  Also, we need to stop doing this work when the user let's go of the mouse button (mouseup). That means we actually have another mouse event handler we need to be notified of so let's go add it in the initApp() method too.  It'll look as simple as this one line for now.

JavaScript
window.addEventListener("mouseup", mouseUpHandler);

The code in the mouseDownHandler() in our new else if {} clause will be very small so I'll show you the final version now.

JavaScript
else if (event.ctrlKey){
            isCtrlKeyPressed = true;
            hitTest(currentPoint);
        }

I've created a new global flag called isCtrlKeyPressed as a simple way to determine if we are handling this point move code.  I've added that variable to the top of the trapponts.js and set it to false by default.  

When the user presses the Ctrl key and clicks the mouse I set it to true and then we call our hitTest() method to determine if we hit a point that is on the grid.  Notice that in this case we don't care about the returned point since the mouseMoveHandler is going to take over and do some work when the Ctrl key is pressed.

Altering the hitTest() Method

To do this move point work I need to alter the hitTest() method just a bit.  It's the easiest way to alter the code to do our work.  First I'll show you the code and then explain what it does.

JavaScript
function hitTest(p){
    // iterate through all points
    for (var x = 0;x<allPoints.length;x++){
        if ((Math.abs(p.x - allPoints[x].x) <= RADIUS) && Math.abs(p.y - allPoints[x].y) <=RADIUS){
            console.log("It's a hit..." + allPoints[x]);
            if (isCtrlKeyPressed){
                capturedIndex = x;
                selectedTriangle = [];
                isCtrlKeyPressed = false;
            }
            return allPoints[x];
        }
    }
}

I added the if statement bolded in the previous code sample.  In previous versions I simply returned the point which was hit.  Now, I set a global variable named capturedIndex to the current index of the point which was found in the allPoints[] array.  This is a simple way to provide a value outside of this method and a bit of a cheat.  

Reset Selected Triangle

You can see that I also set the selectedTriangle to null so that all the red highlighted points will be gone.  I didn't want to deal with updating the selected triangle too so I just reset it.  I can always do that work later and for now this is good enough.  It's not that big of a deal for the user to selecte three points again.

After that I set the isCtrlKeyPressed back to false because we no longer need that value and it will be set again if the user clicks again.

Now, the code gets really fun and really easy.  It is really easy code because we have somewhat of a framework built from our functions.

Framework of Functions Support Our Work

The only other code implemented is for the onMouseMove and onMouseUp methods and they are very simple.

The onMouseUp simply sets the selectedIndex = null to indicate that nothing is selected and mouseMove need not process because the user has not selected any points to move.

The entire method looks like the following:

JavaScript
function mouseUpHandler(event){
    capturedIndex = null;
}

mouseMoveHandler Is Quite Simple

Because of the work we've done before this, the mouseMoveHandler is much simpler than I expected it to be. 

JavaScript
function mouseMoveHandler(event){
    if (capturedIndex !== null){
        allPoints[capturedIndex] = getMousePos(event);
        draw();
    }
}

All we have to do is check to see if the capturedIndex value is not null.  If it is then we know a point has been captured in the hitTest() method.

We then get the current mouse position by calling our getMousePos() (each time the mouse moves it gets updated) and we set the original index of the point in the allPoints[] array to this new point value.  That updates it's x & y values.  After that we call draw() which will draw the background and all the points in allPoints[] array every time the mouse moves.  That effectively makes the point look as if it is moving around on the grid.  

All because our previous methods were written compactly we were able to add this work without destroying everything we did previously.

That's it for this time.  

Keep on learning, keep on coding.

History

First version of this article and the two included code versions (step 7 & 8): 04-27-2016

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

raddevus
Software Developer (Senior) RADDev Publishing
United States United States
Roger has worked in IT for over 25 years in numerous roles (Technical Support, Quality Assurance, Capacity & Performance Engineering and Software Development).
During that time, he has recognized that software often just becomes another layer of work that the user has to wade through.
Sometimes technical documentation is like that too: so confusing and complex that it wastes developers' time.
That's why when he writes his books like Programming Windows 10 Via UWP and his articles (Practical Electronics For Makers) he strives to explain things in the shortest available space with the simplest language possible. Often that means, writing in a tutorial style with numerous images to help guide the user.
He believes the best guiding principle is Einstein's famous quote: "Everything should be made as simple as possible, but not simpler."

Comments and Discussions

 
Questionconvex hull button is not working Pin
Member 1271277631-Aug-16 19:59
MemberMember 1271277631-Aug-16 19:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.